Windows PowerShellシェルのセキュリティ保護

Don Jones

Windows PowerShell チームが新しいシェルの作成に取りかかり、"スクリプト" という用語が議題に上ったとき、おそらくレドモンド中にうなり声が聞こえたことでしょう。結局のところ、管理用スクリプト (ここでは、VBScript のことを言います) に対するマイクロソフトのこれまでの取り組みに、まったく問題がなかったわけではありませんでした。多くの

ユーザーの希望を取り込み、実行モデルに与えた権限が過剰になってしまったため、深刻な問題が発生する可能性が生じました。

チームの仲間たちは Windows PowerShell™ の最初のミーティングで、「同じ道はたどらない」と決意し、これを実現しました。マイクロソフトは、自らのセキュリティについての評判を大きく向上しました。セキュリティについて、製品の開発サイクルの後半ではなく最初の段階で考え始めたことがその最大要因です。この考え方は、Windows PowerShell に非常にはっきりと表れています。

作成したスクリプトが実行されない理由

Windows PowerShell を新しいコンピュータにインストールして .ps1 ファイルをダブルクリックすると、Windows PowerShell ではなくメモ帳が起動します。これは、Windows PowerShell スクリプトに使用される .ps1 というファイル名拡張子が、シェル自体に関連付けられていないためです。つまり、単純に .ps1 ファイルをダブルクリックしただけではスクリプトを実行できません。スクリプトを実行するには、Windows PowerShell を開き、スクリプト名を入力して Enter キーを押すという方法しかありません。

ただし、実際には、スクリプト名を入力するだけでは不十分です。図 1 を見ると、Demo1.ps1 というファイルが現在のフォルダにありますが、「demo1」と入力して Enter キーを押すと、"用語 'demo1' は、コマンドレット、関数、操作可能なプログラム、またはスクリプト ファイルとして認識されません。" というエラーが返されることがわかります。なぜこのような問題が発生するのでしょうか。結局のところ、ファイルはまったく実行されません。

図 1 Windows PowerShell では、コマンドの乗っ取りを防ぐために、スクリプトのパスを指定する必要があります。

図 1** Windows PowerShell では、コマンドの乗っ取りを防ぐために、スクリプトのパスを指定する必要があります。 **(画像を拡大するには、ここをクリックします)

これは、Windows PowerShell セキュリティ モデルのもう 1 つの側面です。Windows PowerShell 以外のシェルで悪意のあるユーザーがよく使用する方法の 1 つに、組み込みコマンドと同じファイル名を持つスクリプトを作成する方法があります。たとえば、作成したスクリプトをあるユーザーに実行させたい場合、そのスクリプトに Dir.ps1 という名前を付けてフォルダに格納します。ユーザーに「Dir」と入力して Enter キーを押せばよいと思わせれば、ユーザーが想定していた Dir コマンドではなく、作成したスクリプトを実行させることができます。この方法は、コマンドの乗っ取りと呼ばれます。Windows PowerShell ではスクリプトのパスを必ず指定する必要があるので、コマンドの乗っ取りに対しては非常に堅牢です。

demo1 はパスを指定していないので実行されませんが、「./demo1」と入力すると実行されます。これは、パス (現在のフォルダ) を指定したためです。組み込みコマンドを参照するつもりなら、どのようなパスも入力することはないでしょうから、このコマンド ラインが組み込みコマンドと間違われることはほとんどありません。このように Windows PowerShell では、必ずパスを指定することで、コマンドの乗っ取りと Enter キーを押したときに発生する可能性がある動作をめぐる混乱を回避しています。

スクリプトの実行におけるポリシー

Windows PowerShell を新しくインストールした後は、適切な構文を使用してスクリプトを実行します。驚いたことに、またしても、Windows PowerShell によるスクリプトの実行が許可されてないことを知らせるエラー メッセージで迎えられます。どういうことでしょう。実は、ここからはシェルの実行ポリシーの領域です。

現在の実行ポリシーは、シェルで Get-ExecutionPolicy を実行することによって確認できます。既定では、現在の実行ポリシーとして Restricted が設定されています。これは、簡単に言うと、スクリプトが実行されないことを意味します。常に、だれが実行しようとしても、です。既定では、Windows PowerShell はスクリプトを実行するためではなく、対話的に実行するためだけに使用できます。Set-ExecutionPolicy コマンドレットを使用すると、以下の 4 つの実行ポリシー設定のいずれかを選択できます。

  • Restricted - 既定の設定です。どのスクリプトも実行できません。
  • AllSigned - 信頼されたスクリプトのみ実行できます (これについては、後で詳しく説明します)。
  • RemoteSigned - ローカル スクリプトであれば、信頼されていなくても実行できます。ただし、インターネットからダウンロードしたスクリプトを実行するには、そのスクリプトを信頼する必要があります。
  • Unrestricted - 信頼されていないスクリプトも含め、すべてのスクリプトを実行できます。

実のところ、運用環境のすべてのコンピュータに設定すべき最も低い設定は、AllSigned です。開発環境やテスト環境では RemoteSigned が役立ちますが、この設定は通常のユーザーには必要ありません。また、どのような場合であれ、Unrestricted が不要であることもわかっているので、Windows PowerShell の今後のバージョンでこのような権限を過剰に与える設定が削除されたとしても気になりません。

信頼に関する質問

コンピュータは、ルート証明機関 (CA) (デジタル証明書を発行するサーバー) を信頼するようにあらかじめ構成されています。図 2 に、信頼されたルート CA が一覧表示された、コントロール パネルにある [インターネット オプション] アプリケーションを示します。Windows Vista® では、この一覧は非常に簡潔で、いくつかの主要な商用のルート CA のみが表示されます。これに対し、Windows® XP の既定の一覧はサイズが大きく、聞いたこともないようなルート CA が多数表示されます。この一覧にルート CA が含まれていれば、原則的に、そのルート CA を運営する会社がデジタル証明書の発行前に行う ID 確認プロセスには問題がなく、その会社を信頼していることを表します。

一般にコード署名証明書と呼ばれるクラス 3 のデジタル証明書を取得する場合、CA (商用の CA または組織内に存在するプライベート CA) によって ID の確認が行われる必要があります。デジタル証明書は、電子パスポートや電子 ID カードのようなものです。CA は、私が Don Jones であることを示す ID を私に与える前に、いくつかの段階を踏んで、本当に私が Don Jones であることを確認する必要があります。自分の証明書を使用して Windows PowerShell スクリプトにデジタル署名する場合は、Set-AuthenticodeSignature コマンドレットを使用して、スクリプトに自分の名前を署名します。もちろん、だれか他人の名前を含む偽の証明書を取得できたとしたら、その人の名前でスクリプトに署名できます。そのため、信頼できる CA のみが 図 2 の一覧に表示されることが非常に重要になります。

図 2 Windows Vista の信頼された既定のルート CA

図 2** Windows Vista の信頼された既定のルート CA **(画像を拡大するには、ここをクリックします)

実行ポリシーの設定が AllSigned および RemoteSigned の場合、スクリプトを信頼できるかどうかが Windows PowerShell によってチェックされるときに、以下の 3 項目についての質問が行われます。

  • そのスクリプトには署名されていますか。署名されていなければ、そのスクリプトは信頼されません。
  • 署名が損なわれていますか。つまり、そのスクリプトは、署名後に変更されていますか。署名が損なわれていれば、そのスクリプトは信頼されません。
  • 署名は信頼されているルート CA によって発行されたデジタル証明書を使用して作成されていますか。そうでなければ、そのスクリプトは信頼されません。

今月のコマンドレット : Set-AuthenticodeSignature

Set-AuthenticodeSignature は、Windows PowerShell スクリプトにデジタル署名するための重要なコマンドレットです。このコマンドレットを使用すると、シェルにおいて最も安全性の高い実行ポリシーである AllSigned を使用できます。このコマンドレットを使用するには、インストールされたコード署名証明書をフェッチする Get-ChildItem という別のコマンドレットを最初に使用します。

$cert = Get-ChildItem –Path cert:\CurrentUser\my –codeSigningCert

ファイルのパスは、インストール済みの証明書を指すパスに置き換える必要があります。インストール済みの証明書には、すべて cert: ドライブを使用してアクセスできます。証明書が読み込まれたら、次のコマンドレットを実行してスクリプトに署名します。

Set-AuthenticodeSignature MyScript.ps1

–cert $cert

もちろん、コマンドレットのパスには、.ps1 スクリプト ファイルへの完全なパスを指定します。シェルによって、ファイルに署名ブロックが追加されます。

信頼によってどのようにセキュリティが強化されるのでしょうか。確かに、ハッカーは、悪意のあるスクリプトを作成してそのスクリプトに署名をし、環境内のだれかに、そのスクリプトを実行することが正しいと思わせることができる可能性があります。そのスクリプトは署名されているので、実行されます。ですが、署名されているので、その署名から作成者の ID を見つけ、警察当局に電話するなどの適切な措置を講じることができます。けれども、悪意のあるスクリプトを作成した後、自分の本当の名前を署名するなんて、ずいぶん間抜けな話ですよね。

もちろん、悪意のあるユーザーがだれか他人の ID の証明書 (適切な ID 確認プロセスを経ずに CA が発行する証明書など) を入手できたとしたら、その署名を使用して実際の犯人を特定することはできないでしょう。これが、信頼するルート CA に、満足できる ID 確認プロセスが必要とされる理由です。

AllSigned 実行ポリシー設定を使用する場合、自分が作成したすべてのスクリプトについても、実行する前にデジタル署名をしておく必要があります。Set-AuthenticodeSignature コマンドレットは非常に簡単に使用できますが、それでも、多くの煩わしさが伴います。その点においては、サードパーティのソフトウェアが役に立つ場合があります。指定した証明書を使用してスクリプトに自動的に署名を行うスクリプト エディタかビジュアルな開発環境を使用することをお勧めします。それらを使用すると、余計な作業を必要とせず、あまり意識しないで署名を使用できます。このような作業をサポートするエディタや開発環境を希望する場合は、私が運営する SscriptingAnswers.com のようなスクリプトのオンライン フォーラムをいくつか参照し、他の Windows PowerShell ファンがどのようなものを使用しているかを問い合わせるメッセージを投稿してください。

証明書が入手できたら、次のコードを実行して、特定の場所にあるすべてのスクリプトに署名することもできます。

Get-Childitem *.ps1 | %{Set-AuthenticodeSignature $_.fullname $cert}

一元管理されるセキュリティ保護

当然のことですが、Windows PowerShell の実行ポリシーは、コンピュータごとに構成できます。しかし、企業環境にとって、これは適切な解決策とは言えません。代わりに、グループ ポリシーを使用できます (go.microsoft.com/fwlink/?LinkId=93675 から Windows PowerShell の管理用テンプレートをダウンロードできます)。ドメイン全体に適用されるグループ ポリシー オブジェクト (GPO) に、単純にファイルを配置し、Restricted に設定します。これにより、Windows PowerShell をインストールした場所にかかわりなく、スクリプトが実行されないようにすることができます。その後、より制限の少ない設定 (AllSigned) を、スクリプトを実行する必要があるコンピュータ (独自のワークステーションなど) にのみ適用することができます。

代替の資格情報

以前の Windows 用の管理用スクリプト言語とは異なり、Windows PowerShell ではセキュリティで保護された方法で代替の資格情報を処理することもできます。たとえば、Get-WMIObject コマンドレットには –credential パラメータがあり、このパラメータを使用すると、リモート WMI 接続に代替の資格情報を指定できます。このパラメータは、ユーザー名は受け取りますがパスワードは受け取りません。そのため、慎重に扱う必要があるパスワードを、クリア テキスト スクリプトにハードコードする必要がありません。代わりに、ユーザー名を指定したときに、Windows PowerShell のダイアログ ボックスが表示され、パスワードの入力を求められます。代替の資格情報を再利用する場合は、次の Get-Credential コマンドレットを使用して、認証情報を格納できます。

$cred = Get-Credential DOMAIN\USER

すぐにパスワードの入力が求められ、最後の資格情報が変数 $cred に格納されます。その後、変数 $cred を必要なだけ何度でも –credential パラメータに渡すことができます。ConvertTo-SecureString コマンドレットと ConvertFrom-SecureString コマンドレットを使用して、資格情報を暗号化された文字列に変換することや、既に暗号化されている文字列を資格情報に変換することもできます。

これには、暗号化キーが最終的にプレーン テキストのスクリプトに保存されるため、セキュリティをまったく無意味にしてしまうという問題が伴います。そこで代わりの方法として、Get-Credential に対する呼び出しを Windows PowerShell プロファイルに追加することができます。これにより、Windows PowerShell を実行したときに即座にユーザー名とパスワードの入力が求められ、入力したユーザー名とパスワードは $cred という変数に格納されます。このようにして、ドメインの管理者アカウントを表す変数 $cred を、いつでもシェルで使用することができます。また、その資格情報の格納先に悩む必要は一切ありません。資格情報はシェルを起動するたびに再作成されるので、格納する必要がないのです。

既定で安全

既定では、Windows PowerShell は、スクリプトをサポートする管理用シェルとして考えられる限り、最もセキュリティの高い状態でインストールおよび構成されます。このような設定を変更しない限り、Windows PowerShell では最大限のセキュリティ レベルが保たれます。つまり、Windows PowerShell のセキュリティ レベルが下がるのは、ユーザーの決定と操作に起因します。そのため、ファイル拡張子の関連付けの再構成や実行ポリシーの変更といった、既定設定の変更を行う前に、必ず操作の結果を完全に把握し、変更によって問題が発生した場合の対策を検討しておく必要があります。

Don Jones は SAPIEN Technologies のリード スクリプト グルで、ScriptingTraining.com のインストラクタです。Don に対するお問い合わせについては、彼の Web サイト (ScriptingAnswers.com) を参照してください。

© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; 許可なしに一部または全体を複製することは禁止されています.