PowerShellの実行ポリシーとスクリプト署名 ── 「Bypassで蓋をする」運用から卒業する実務ガイド

· 更新日: · · PowerShell, Windows, 実行ポリシー, コード署名, セキュリティ, スクリプト, 運用改善, 自動化

更新履歴(4件・最終更新 2026年08月02日)

この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。

記事の冒頭に「この記事の知識マップ」節を追加しました。本文で扱っている概念とその関係を、要約・図・詳細ページへのリンクにまとめたものです。本文の主張は変えていません。
外部レビュー(1283件)への対応として本文を更新しました。個々の変更内容は、この下の履歴を参照してください。
ワークグループ環境について「自己署名証明書はワークグループでの配布には使えない」と断定していたのを直しました。公式ドキュメントの「他のコンピューターでは実行できない」は既定の状態の説明で、その端末が証明書を信頼していないから動かない、という意味です。公開部分だけを各端末の「信頼されたルート証明機関」と「信頼された発行元」へ配れば、秘密鍵を署名端末から出さないままAllSignedを通せます。端末が10台20台の会社に商用CAの購入を強いる書き方だったので、配布手順のコード例と、引き受けるコスト(秘密鍵の保護、失効手段が無いこと、期限切れ時の再配布、新規端末の手順)を商用CAと並べた表を追加しました。
社内CAで発行する場合に次に何をするかの節を新設しました(CAの有無の確認、コード署名テンプレートの複製、`Get-Certificate`での要求、GPOからの信頼配布)。ドメインに参加していない環境の節、代替データストリームがNTFSの機能であるという説明、`Cert:`ドライブの解説を追加し、タイムスタンプURLを実在のサービス例に置き換えたうえで、AD CSの役割サービスにタイムスタンプ応答が含まれないことを明記しました。
初版公開
この記事を引用する(DOI: 10.5281/zenodo.21590109)

この記事はZenodoにアーカイブされています。常に最新版へ解決されるDOIと、いま表示している版に固定されたDOIの両方を下に示します。

小村 豪(2026)「PowerShellの実行ポリシーとスクリプト署名 ── 「Bypassで蓋をする」運用から卒業する実務ガイド」合同会社小村ソフト. https://doi.org/10.5281/zenodo.21590109 https://staging.comcomponent.com/blog/powershell-execution-policy-script-signing/

DOI(最新版)
10.5281/zenodo.21590109
DOI(この版)
10.5281/zenodo.21733069

「新しいPCでスクリプトが『このシステムではスクリプトの実行が無効になっているため…』と言われて動かない」「とりあえず -ExecutionPolicy Bypass を付けたら動いたので、全部のタスクにそう書いてある」「共有フォルダーに置いた .ps1 が、ある人の端末でだけ署名エラーになる」── PowerShellの実行ポリシーは、社内の自動化を進めるとほぼ確実にぶつかる壁です。そして多くの現場で、仕組みを理解しないまま「Bypassで蓋をする」対処が積み重なっています。

厄介なのは、実行ポリシーが「何を守るための機能なのか」が誤解されやすいことです。セキュリティ機能だと思って厳しくしすぎて業務が止まる。逆に「どうせ意味がない」と全部Bypassにして、うっかり事故を防ぐ最後の安全装置まで外してしまう。どちらも仕組みを知っていれば避けられます。

この記事では、中小企業の情シス担当者や、PowerShellで社内の定型作業を自動化している方を対象に、実行ポリシーの正体とスコープの優先順位、Zone.Identifier(いわゆるMark of the Web)との関係、そしてスクリプト署名による配布運用までを、公式ドキュメントの裏付けつきで整理します。

1. まず結論

  • 実行ポリシーはセキュリティ境界ではなく安全装置です。公式ドキュメントは「実行ポリシーはユーザーの操作を制限するセキュリティシステムではない」「スクリプトの内容をコマンドラインに入力すれば簡単に迂回できる」と明言しています。目的は、基本ルールを決めて意図しない実行(うっかり事故)を防ぐことです。1
  • 実行ポリシーが影響するのはスクリプトの実行だけで、対話的なコマンド実行は常に可能です。Windows PowerShell 5.1の既定はクライアントOSでRestricted(スクリプト実行不可)、Windows ServerでRemoteSignedです。PowerShell 7ではRemoteSignedが既定とされています。21
  • ポリシーには5つのスコープがあり、優先順位は MachinePolicy > UserPolicy > Process > CurrentUser > LocalMachine です。MachinePolicy/UserPolicyはグループポリシー専用で、Set-ExecutionPolicy では変更できず、コマンドラインの指定でも上書きできません。13
  • RemoteSignedが署名を要求するのは「インターネット由来」のスクリプトだけです。由来の判定はファイルに付くZone.Identifier代替データストリーム(Mark of the Web)で行われ、内容を確認したうえで Unblock-File で除去すれば、ポリシーを変えずに実行できます。14
  • AllSignedはローカル作成分も含むすべてのスクリプトに信頼された発行元の署名を要求します。署名は Set-AuthenticodeSignature で付与し、ファイル末尾に # SIG # のコメントブロックとして埋め込まれます。15
  • 署名には必ずタイムスタンプ(-TimestampServer)を付けます。タイムスタンプがあれば、署名証明書の期限が切れた後もスクリプトが有効であり続けます。コード署名証明書の多くは有効期間1年なので、これを省くと毎年の時限爆弾になります。65
  • 自己署名証明書はテスト用途限定です。自己署名で署名したスクリプトは他のコンピューターでは実行できません。組織配布には証明機関(社内CAまたは商用CA)発行のコード署名証明書を使います。57
  • 組織で統制するならグループポリシーの「スクリプトの実行を有効にする」で一元管理します。この設定はPowerShell側のすべてのスコープ設定に優先します。1

この記事の知識マップ

PowerShellの実行ポリシーはセキュリティ境界ではなく安全装置で、複数のスコープを持ち、グループポリシーの設定がすべてに優先します。RemoteSignedはインターネット由来を示すZone.Identifier(Mark of the Web)が付いた未署名スクリプトだけをブロックし、内容を確認できればUnblock-Fileで印を除去して通せます。より厳格なAllSignedやスクリプト配布時の署名にはコード署名証明書が必要で、Set-AuthenticodeSignatureとタイムスタンプサーバーの指定により、証明書の期限切れ後も署名を有効に保てます。自己署名証明書を各端末の信頼ストアへ配れば配布は可能ですが、その証明書自体が信頼の起点になるため秘密鍵の漏えいリスクを引き受けることになり、常用のBypass指定はグループポリシー管理下では効かず安全装置を放棄する運用になります。

PowerShellの実行ポリシーとスクリプト署名の知識マップ実行ポリシーのグループポリシーによる優先、RemoteSignedとZone.Identifier(Mark of the Web)の関係、Unblock-Fileによるブロック解除、コード署名証明書によるSet-AuthenticodeSignatureとタイムスタンプ、自己署名証明書配布のリスクの関係を示す図利用するで構成できる両立しない利用するに保存される防止する前提とする前提とする前提とする利用する利用するに保存される前提とする原因になり得るに保存される利用する利用する用いるのは非推奨PowerShell実行ポリシーコード署名証明書PowerShellグループポリシーBypassポリシーRemoteSignedポリシーZone.Identifier(Mark of the Web)代替データストリーム(ADS)Unblock-File管理者権限実行ポリシーAllSignedSet-AuthenticodeSignatureコード署名のタイムスタンプCert:ドライブアドホックな自己署名証明書信頼された発行元ストア信頼の起点の悪用リスクタスクスケジューラの無人実行

図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全18件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle

2. 実行ポリシーの正体 ── 「セキュリティ境界」ではなく「安全装置」

まず前提の確認です。公式ドキュメント(about_Execution_Policies)の記述は率直です。実行ポリシーは、PowerShellが構成ファイルを読み込みスクリプトを実行する条件を制御する「安全機能(safety feature)」であり、「ユーザーの操作を制限するセキュリティシステムではない」。スクリプトを実行できないユーザーでも、スクリプトの内容をコマンドラインに貼り付ければ同じ処理を実行できるからです。実行ポリシーの役割は、基本ルールを設定し、それに意図せず違反することを防ぐことだ、と明記されています。1 入門ドキュメントでも「セキュリティ境界ではない。意図的にスクリプトを実行しようとするユーザーは止められない」と繰り返されています。2

この位置づけを押さえると、運用の設計方針が定まります。「実行ポリシーを厳しくしたから攻撃は防げる」も「どうせ迂回できるから無意味」も、どちらも間違いです。攻撃対策は別レイヤー(アプリケーション制御、権限最小化、監査ログ)の仕事、実行ポリシーは事故防止の仕事です。8

主要なポリシーの違いを整理します。1

ポリシー スクリプト実行 署名要求 位置づけ
Restricted 不可(個別コマンドのみ) Windows PowerShell 5.1のクライアントOS既定2
AllSigned すべてのスクリプト・構成ファイルに要求。未分類の発行元は実行前に確認 署名運用ができる組織向け
RemoteSigned インターネット由来のスクリプトにのみ要求。ローカル作成分は不要 実務の標準。PowerShell 7の既定1
Unrestricted なし(イントラネットゾーン外は警告) 非Windowsの既定(変更不可)1
Bypass なし。警告もプロンプトもなし PowerShellを土台に独自のセキュリティモデルを持つアプリ組み込み用1

見落とされがちですが、Restrictedは業務スクリプトだけでなく、プロファイル(.ps1)やモジュール(.psm1)、書式構成ファイル(.ps1xml)の読み込みまで止めます。1「新しい端末でプロファイルが読み込まれない」の原因が実行ポリシーだった、というのは定番の相談です。

3. スコープと優先順位 ── 「設定したのに変わらない」の正体

実行ポリシーは1つの値ではなく、5つのスコープごとに設定でき、優先順位の高いものが実効値になります1

スコープ 設定手段 保存先 優先順位
MachinePolicy グループポリシー(コンピューターの構成) GPO 1(最優先)
UserPolicy グループポリシー(ユーザーの構成) GPO 2
Process -ExecutionPolicy 起動パラメーター / -Scope Process 環境変数 $Env:PSExecutionPolicyPreference(セッション終了で消滅) 3
CurrentUser Set-ExecutionPolicy -Scope CurrentUser ユーザーの構成 4
LocalMachine Set-ExecutionPolicy(既定スコープ、要管理者) 全ユーザー共通の構成 5

「設定したのに変わらない」トラブルの切り分けは、実効値ではなく一覧を見るのが定石です。

# 実効ポリシーだけでなく、どのスコープが効いているかを必ず確認する
Get-ExecutionPolicy -List

# 例: LocalMachineをAllSignedにしていても、CurrentUserのRemoteSignedが勝つ
#         Scope ExecutionPolicy
#         ----- ---------------
# MachinePolicy       Undefined
#    UserPolicy       Undefined
#       Process       Undefined
#   CurrentUser    RemoteSigned
#  LocalMachine       AllSigned

# 自分の環境だけ変えるなら管理者権限不要のCurrentUserスコープが手軽
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

CurrentUserはLocalMachineより優先されるため、上の例の実効ポリシーはRemoteSignedです。1 Set-ExecutionPolicy は既定でLocalMachineスコープに書き込むため管理者権限が必要ですが、CurrentUserスコープなら一般ユーザーでも変更できます。3

そして組織管理の本命がグループポリシーです。「スクリプトの実行を有効にする(Turn on Script Execution)」ポリシーは、PowerShell側で設定したすべてのスコープに優先します。無効にするとRestricted相当、有効にすると「すべてのスクリプトを許可(Unrestricted)」「ローカルスクリプトとリモートの署名済みスクリプトを許可(RemoteSigned)」「署名済みスクリプトのみを許可(AllSigned)」から選択でき、管理用テンプレートの Windowsコンポーネント\Windows PowerShell 配下にあります。コンピューターの構成はユーザーの構成に優先します。1 GPOで管理された環境では Set-ExecutionPolicy は設定を保存こそすれ実効せず、競合を説明するメッセージが表示されます。3

ここで重要な帰結がひとつ。GPOで管理していれば、タスクやショートカットに書かれた -ExecutionPolicy Bypass は効きません。Processスコープの指定はLocalMachine/CurrentUserの構成には勝ちますが、グループポリシーには勝てないと明記されています。1「Bypassを書けば何とかなる」が通用するのは、逆に言えば組織として実行ポリシーを管理していない環境だけです。

4. Zone.Identifier(Mark of the Web)とUnblock-File

RemoteSignedの「リモート(インターネット由来)」は、ファイルの置き場所ではなくで判定されます。ブラウザーなどのプログラムはダウンロードしたファイルに代替データストリームを付加し、「インターネットから来たファイル」としてマークします。1 このストリームがZone.Identifierで、インターネットゾーンを示す値3が入っています。いわゆるMark of the Webです。4

代替データストリーム(Alternate Data Streams)はNTFSの機能です。NTFSでは1つのファイルが複数のデータストリームを持つことができ、いつも見ている「ファイルの中身」は名前のない既定のストリームに入っています。それとは別に、ファイル名:ストリーム名という書き方で名前付きのストリームを付けられる、という仕組みです。9 Zone.Identifierはその名前付きストリームの1つなので、スクリプト本文は1バイトも変わらないのに実行可否だけが変わります。中身が同じでも端末によって動いたり動かなかったりするのはこのためで、逆にNTFS以外のファイルシステム(USBメモリーのFAT32など)を経由するとストリームごと落ちるため、印が消えて動くようになることもあります。

RemoteSigned環境でこの印付きの未署名スクリプトを実行しようとすると、「デジタル署名されていません」というエラーでブロックされます。対処は2段階です。

# 1) まずどのファイルがブロックされているかを確認する(読み取りだけの安全な操作)
Get-Item -Path C:\Tools\*.ps1 -Stream Zone.Identifier -ErrorAction SilentlyContinue

# 2) 内容をレビューして安全だと判断できたものだけブロック解除する
#    (Unblock-FileはZone.Identifierストリームを除去する。実行ポリシー自体は変わらない)
Unblock-File -Path C:\Tools\Get-InventoryReport.ps1

Unblock-File はZone.Identifier代替データストリームを除去するコマンドレットで、エクスプローラーのプロパティにある「許可する(Unblock)」ボタンと同じ操作です。実行ポリシーを緩めずに、確認済みのファイルだけを通せるのがポイントです。4 公式ドキュメントも「使う前にファイルと入手元を確認し、安全であることを検証すること」を必須の手順としています。43

逆方向の落とし穴も知っておく価値があります。すべての入手経路がMark of the Webを付けるわけではありません。curl.exeInvoke-WebRequestInvoke-RestMethod でダウンロードしたファイルにはインターネットゾーンの印が付かないことがあると明記されています。1 つまりRemoteSignedは「ダウンロードした危険なファイルをすべて止める」保証ではありません(だからこそ安全装置なのです)。また、UNCパスとインターネットパスを区別しない構成のシステムでは、共有フォルダー上のスクリプトがRemoteSignedで実行を拒否されることがある、という注意書きもあります。1「共有に置いた .ps1 が一部の端末でだけ止まる」ときは、ゾーン設定とZone.Identifierの有無を疑ってください。

なお、ダウンロードした実行ファイル(.exe)側で似た症状を起こすSmartScreenの仕組みは「Windowsで「Windows によって PC が保護されました」が出る理由」で扱っています。

5. スクリプト署名の実務 ── Set-AuthenticodeSignatureとタイムスタンプ

AllSigned運用、あるいはRemoteSigned環境で配布物に署名する運用に進むには、コード署名証明書が必要です。入手経路は3つに整理できます。5

入手経路 信頼される範囲 判断
自己署名(New-SelfSignedCertificate) 自分のコンピューターのみ。他の端末では実行不可 テスト・検証専用。配布には使わない57
社内CA(証明機関)発行 社内CAを信頼するよう構成した組織内の端末 ADドメイン環境の本命。証明書の発行・失効を組織で統制できる
商用CA発行(有償) Windows全般(パブリックCAを信頼済み) 社外にスクリプトを配布する場合

公式ドキュメントの整理も同じで、「証明機関が発行する証明書は他のコンピューターでも信頼される」「自己作成の証明書は無料だが自分のコンピューター専用であり、テスト目的に限定すべき」とされています。5 社内配布が目的なら、Active Directory証明書サービス(AD CS)などの社内CAからコード署名証明書を発行するのが現実的です。

社内CAで発行する場合、次に何をするか

「社内CAが現実的」で話を終わらせると次の一歩が踏み出せないので、段取りを書いておきます。AD CSは公開鍵基盤(PKI)を提供するWindows Serverの役割で、証明機関(CA)の役割サービスのほか、Web登録やオンライン応答者(OCSP)などの役割サービスから構成されます。10

  1. 社内CAがあるかを確認する。まず、社内にAD CSのエンタープライズCAがあるかを情シス側で確認します。ない場合、コード署名のためだけにCAを新設するのは重い判断です。証明書1枚の購入費用と、CAの構築・バックアップ・失効リスト(CRL)の公開・鍵の保護といった運用の手間を比べて、商用CAの購入と天秤にかけてください。
  2. コード署名用の証明書テンプレートを用意してもらう。CA側の作業です。既定の「コード署名」テンプレートを複製し、複製したほうで有効期間・キーの長さ・申請できるセキュリティグループ(署名担当者だけに絞る)を設定して、「証明機関」管理コンソールで発行対象のテンプレートに追加してもらいます。既定のテンプレートを直接編集しないのは、ドメイン全体に影響が出て元に戻しにくいためです。
  3. 署名担当者が証明書を要求する。署名する端末で、Get-Certificateコマンドレットにテンプレート名を指定して要求します。発行されればそのまま個人ストアに入ります(このコマンドレットが証明書を格納できるのはMyストアだけです)。11

    # 社内のエンタープライズCAへ、テンプレート名を指定して要求する(Windows統合認証)
    # 'CodeSigning' の部分は、CA管理者が発行対象として公開したテンプレート名に置き換える
    $result = Get-Certificate -Template 'CodeSigning' -Url ldap: `
        -CertStoreLocation Cert:\CurrentUser\My
    $result.Status        # Issued なら発行済み、Pending なら承認待ち
    
  4. 信頼を各端末へ配布する。社内CAのルート証明書を「信頼されたルート証明機関」ストアへ、署名に使う発行元の証明書を「信頼された発行元」ストアへ、グループポリシー(コンピューターの構成 > ポリシー > Windowsの設定 > セキュリティの設定 > 公開キーのポリシー)で配布します。公開キーのポリシー配下の各ストアを右クリックして証明書をインポートする手順が公式に案内されています。12 ここまでやると、AllSigned環境で出る「この信頼されていない発行元からのソフトウェアを実行しますか?」のプロンプトを運用から消せます。15

CA側(手順2)は情シスだけで完結しない場合が多いので、手順1の確認と手順4の配布方針を先に決めてからCA管理者に相談すると話が早く進みます。

ドメインに参加していない環境(ワークグループ)ではどうするか

対象読者である中小企業では、そもそもADドメインがない・一部の端末だけ参加していない、というケースもあります。GPOも社内CAも使えない前提では、次の順で現実解を積みます。

  • 実行ポリシーは端末ごとに設定し、揃っていることを確認するSet-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser(全ユーザーに効かせるなら管理者権限でLocalMachine)をキッティング手順書に入れます。3 GPOによる強制がない以上、端末ごとに勝手に変えられる余地は残るので、Get-ExecutionPolicy -Listの結果を棚卸しのタイミングで確認する運用とセットにします。
  • スクリプトの正本を1か所にして、書き込みを絞る。共有フォルダーの1か所を正本と決め、書き込み権限は管理者だけにします。実行ポリシーが防ぐのは「うっかり」だけなので、改変を防ぐのはNTFSのアクセス権の仕事です。
  • 署名まで求めるなら、自己署名証明書を配るか、商用CAを買うかを費用で比べる。公式ドキュメントは「自己署名証明書で署名したスクリプトは他のコンピューターでは実行できない」「共有したいスクリプトには適さない」としています。5 ただしこれは既定の状態の説明で、他の端末がその証明書を信頼していないから動かない、という意味です。裏を返すと、信頼のほうを配れば動きます。次の項で分けて整理します。
  • Mark of the Webの扱いを手順書に書く。メールや外部の共有経由で受け取った.ps1には印が付いていることがあるので、「内容をレビューしてからUnblock-File」までを手順として明文化します(4章)。4

自己署名証明書をワークグループで使うなら

「自己署名は署名した端末でしか使えない」で止めると、端末が10台20台の会社に商用CAの購入を強いることになります。実際には、証明書の公開部分を各端末へ配れば AllSigned は通ります。GPOの代わりに手作業・インストーラー・MDM(Intuneなど)で配る、というだけの違いです。

配るのは公開部分だけで、秘密鍵は署名する端末から出しません。

# 【署名する端末で1回】公開部分だけをエクスポートする(-Certは秘密鍵を含まない)
$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert |
        Where-Object Subject -eq 'CN=KomuraSoft Code Signing (Test)'
Export-Certificate -Cert $cert -FilePath .\KomuraSoftCodeSigning.cer

# 【各端末で1回・要管理者権限】ルートと発行元の両方へ入れる。
# ルートに入れないと署名の検証自体が通らず(自己署名なので自分がルート)、
# 発行元に入れないとAllSignedのプロンプトが出続ける
Import-Certificate -FilePath .\KomuraSoftCodeSigning.cer `
    -CertStoreLocation Cert:\LocalMachine\Root
Import-Certificate -FilePath .\KomuraSoftCodeSigning.cer `
    -CertStoreLocation Cert:\LocalMachine\TrustedPublisher

そのうえで、引き受けるコストを先に見ておいてください。商用CAとの差はここに出ます。

論点 自己署名を配る 商用CAを買う
初期費用 0円。ただし全端末への配布作業が要る 証明書の購入費(年額)
秘密鍵の保護 署名端末が丸ごと防衛線。この鍵が漏れると、配布先の全端末が信頼するコードに署名できる。鍵の置き場所と持ち出し禁止を決めておく 同じく重要だが、HSMやクラウド署名サービスの選択肢がある
失効 手段がない。漏れたら各端末からストアを削除して回る CRL/OCSPで失効させられる
期限切れ 更新のたびに全端末へ配り直す(タイムスタンプを付けておけば、既存の署名は期限後も有効です5) 端末側の作業は不要
新しい端末 キッティング手順に組み込む必要がある 不要
「信頼されたルート」に入れる影響 その証明書1枚を端末が信頼する。CAではないので下位の証明書を発行することはできないが、その鍵で署名したものは何でも通る 変更なし

端末が少なく、キッティング手順が管理されていて、署名端末を守れるなら、自己署名を配る構成で足ります。逆に、端末が増え続ける、キッティングが属人的、署名端末が誰でも触れる ── このどれかに当てはまるなら、失効と更新の面倒を商用CAへ外注する判断のほうが安く付きます。

証明書ストアとCert:ドライブ

署名のコードに入る前にCert:について一言。Cert:はPowerShellの証明書プロバイダーが提供する仮想ドライブで、証明書ストアをファイルシステムのようにパスでたどれます。13 構造はCert:\<ストアの場所>\<ストア名>の2階層で、ストアの場所はCurrentUser(ログオン中のユーザー用)とLocalMachine(コンピューター全体用)の2つ、ストア名はMy(個人)、Root(信頼されたルート証明機関)、TrustedPublisher(信頼された発行元)などです。証明書1枚1枚は拇印(Thumbprint)で識別され、Get-ChildItemで一覧できます。13

つまりCert:\CurrentUser\Myは「現在のユーザーの個人ストア」で、次に出てくるGet-ChildItem -Path Cert:\CurrentUser\My -CodeSigningCertは「そこにあるコード署名用の証明書を全部出せ」という意味です。-CodeSigningCertは、拡張キー使用法(EKU)にコード署名を持つ証明書に絞るための証明書プロバイダー固有のパラメーターです。13

まずテスト用の自己署名証明書で流れを確認します。

# テスト用のコード署名証明書を作る(自己署名 ── この端末でしか信頼されない)
$params = @{
    Subject           = 'CN=KomuraSoft Code Signing (Test)'
    Type              = 'CodeSigningCert'
    CertStoreLocation = 'Cert:\CurrentUser\My'
    HashAlgorithm     = 'sha256'
}
$cert = New-SelfSignedCertificate @params

New-SelfSignedCertificate はテスト目的の自己署名証明書を作成するコマンドレットで、-Type CodeSigningCert を指定するとコード署名用の拡張が入ります。有効期間は既定で1年です。7 自己署名証明書をAllSigned環境のテストに使う場合は、その端末の信頼されたルート証明機関ストアへの登録が必要です。5

署名の本番はこの1行です。

# 証明書ストアからコード署名証明書を取得して署名する
# -CodeSigningCertは「コード署名に使える証明書」に絞るだけなので、
# 期限内で秘密鍵を持つものに限定し、複数ある環境ではSubjectやThumbprintで特定する
$cert = Get-ChildItem -Path Cert:\CurrentUser\My -CodeSigningCert |
    Where-Object { $_.HasPrivateKey -and $_.NotAfter -gt (Get-Date) -and
                   $_.Subject -eq 'CN=KomuraSoft Code Signing (Test)' } |
    Sort-Object NotAfter -Descending |
    Select-Object -First 1   # 更新などで同じSubjectが複数あるときは有効期限が最も先のもの1枚に絞る

# タイムスタンプは必須。証明書の期限が切れても署名が有効であり続ける
# URLはRFC 3161対応のタイムスタンプサービスを指定する。証明書の発行元(購入先)の案内が最優先。
# 例: DigiCertが公開しているタイムスタンプサーバー http://timestamp.digicert.com
Set-AuthenticodeSignature -FilePath .\Invoke-NightlyBatch.ps1 -Certificate $cert `
    -HashAlgorithm SHA256 -TimestampServer 'http://timestamp.digicert.com'

# 署名の状態はGet-AuthenticodeSignatureで確認できる(Valid/NotSigned/HashMismatchなど)
# TimeStamperCertificateが空でなければ、タイムスタンプが付いている
Get-AuthenticodeSignature -FilePath .\Invoke-NightlyBatch.ps1 |
    Format-List -Property Status, SignerCertificate, TimeStamperCertificate

押さえるべき仕様が3つあります。65

  • 署名はファイル末尾の # SIG # コメントブロックとして埋め込まれます。既存の署名があれば置き換えられます。つまり、署名後にスクリプトを1文字でも書き換えれば署名は無効になります。これが改変検知として機能します。
  • -TimestampServer を指定すると、証明書の有効期限が切れてもスクリプトが失敗しなくなります。署名の有効性は「署名証明書が有効な間」または「証明書が有効だった時点で署名されたことをタイムスタンプサーバーが検証できる限り」であり、コード署名証明書の多くは有効期間1年なので、タイムスタンプが長期運用の生命線です。65 URLは架空の値ではなく、実在するRFC 3161対応のサービスを指定します。第一候補は証明書の発行元(購入先のCA)が案内しているURLで、たとえばDigiCertはhttp://timestamp.digicert.comを公開しています。14 なお、AD CSの役割サービスにタイムスタンプ応答用のものは含まれていない10ため、社内CA発行の証明書で署名する場合も、タイムスタンプの取得先は外部のサービスを使う構成になるのが通常です。ここは社内CAの導入検討時に見落とされがちなので、ネットワーク的にそのURLへ到達できるか(プロキシ・ファイアウォールの許可)まで含めて確認してください。署名後にGet-AuthenticodeSignatureTimeStamperCertificateが空でないことを見れば、実際にタイムスタンプが付いたかを確認できます。
  • Windows PowerShell 5.1やPowerShell 7.2より前で署名するスクリプトは、ASCIIまたはUTF8NoBOMで保存が必要でした。PowerShell 7.2以降は任意のエンコーディングで署名済みスクリプトをサポートします。5 5.1が残る現場では、日本語コメント入りスクリプトのエンコーディングで署名検証が壊れる事故が起きやすいので要注意です。5.1と7の違い全般は「Windows PowerShell 5.1とPowerShell 7の違いと移行」を参照してください。

AllSigned環境では、署名済みでも発行元をまだ信頼済み/拒否に分類していない場合、実行時に「この信頼されていない発行元からのソフトウェアを実行しますか?」というプロンプトが出ます。「常に実行する」を選ぶと以後その発行元では確認されません。15 社内CA運用ではあわせて発行元証明書を各端末の「信頼された発行元」に配布しておくと、この確認を運用から消せます。

6. 実務の定石(判断表) ── 「Bypassで蓋」から署名運用まで

社内スクリプトの配布・実行の統制は、次の判断表で考えるのが定石です。

論点 選択肢 判断の目安
環境側のポリシー Restrictedのまま / RemoteSigned / AllSigned 自動化を進めるならRemoteSignedが下限。署名体制があるならAllSigned1
設定の配布 各自でSet-ExecutionPolicy / グループポリシー ドメイン環境ならGPO一択。全スコープに優先し、勝手な変更も封じられる1
配布物の信頼 無署名+共有内配置 / コード署名 無署名運用は改変を検知できない。定期実行される運用スクリプトから順に署名へ
証明書 自己署名 / 社内CA / 商用CA 社内配布は社内CA。自己署名はテスト限定、社外配布は商用CA5
ダウンロード物の扱い ポリシーを緩める / レビューしてUnblock-File ポリシーは触らず、確認済みファイルだけ通す4
定期タスクの起動 -ExecutionPolicy Bypass を常用 / 環境ポリシー整備+署名 Bypass常用は安全装置の放棄。GPO管理下ではそもそも効かない1

最後の行を補足します。「ExecutionPolicy Bypassで蓋をする」運用の問題は、セキュリティホールを開けることそのものではありません(実行ポリシーはもともと境界ではないので)。問題は次の3点です。

  • 安全装置の放棄 ── うっかり別のスクリプトを実行する事故、改変されたスクリプトに気づかず実行する事故を防ぐ最後の網を、恒久的に外すことになります。
  • 統制との乖離 ── GPOで実行ポリシーを管理し始めた瞬間、Bypass指定は効かなくなり、蓋をしていたタスクが一斉に失敗します。1「動いていた理由」が管理外の抜け道だった、という技術的負債です。
  • 原因調査の攪乱 ── Bypassが散在すると、環境の実効ポリシーから動作を推測できなくなり、端末ごとの差異調査(なぜこの端末だけ失敗するのか)が難航します。

Bypass自体は、PowerShellを組み込んだアプリケーションが独自のセキュリティモデルで統制している構成のために用意された設定です。1 人が書いた運用スクリプトの起動オプションに常用するものではない、と割り切ってください。タスクスケジューラでの安全な定期実行の組み立ては「タスクスケジューラのタスクが実行されない・0x1で終わる ── 原因の切り分けと安全な運用設計」で詳しく扱っています。

7. まとめ

  • 実行ポリシーはセキュリティ境界ではなく安全装置です。攻撃対策は別レイヤーで行い、実行ポリシーには「うっかり事故の防止」を担わせます。
  • ポリシーは5つのスコープを持ち、MachinePolicy > UserPolicy > Process > CurrentUser > LocalMachine の順に優先されます。切り分けは Get-ExecutionPolicy -List から始めます。
  • RemoteSignedが見ているのはZone.Identifier(Mark of the Web)です。確認済みのファイルは Unblock-File で、ポリシーを緩めずに通します。
  • 署名は Set-AuthenticodeSignature で行い、-TimestampServer を必ず指定します。証明書は社内配布なら社内CA発行、自己署名はテスト限定です。
  • 組織統制はグループポリシーの「スクリプトの実行を有効にする」で一元化します。この設定はすべてのスコープと -ExecutionPolicy 指定に優先します。
  • -ExecutionPolicy Bypass の常用は安全装置の放棄であり、GPO管理下では効きません。環境ポリシーの整備と署名運用で「蓋」を不要にするのが正道です。

関連記事

関連する相談領域

合同会社小村ソフトでは、社内PowerShellスクリプトの実行ポリシー・署名運用の設計、グループポリシーによる展開の検討、「特定の端末でだけスクリプトが動かない」といった環境差異の調査を扱っています。既存のバッチ・スクリプト資産を安全な運用に載せ替える支援も可能です。

参考リンク

  1. Microsoft Learn, about_Execution_Policies. 実行ポリシーが安全機能でありユーザーを制限するセキュリティシステムではないこと、各ポリシー(AllSigned/Bypass/RemoteSigned/Restricted/Unrestricted等)の定義、5つのスコープと優先順位、Processスコープが$Env:PSExecutionPolicyPreferenceに保存されGPOには勝てないこと、グループポリシー「Turn on Script Execution」が全スコープに優先すること、ダウンロードファイルへの代替データストリーム付加とcurl.exe等では印が付かない場合があること、UNCパスの注意書きについて。  2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

  2. Microsoft Learn, Chapter 1 - Getting started with PowerShell. 実行ポリシーがセキュリティ境界ではないこと、Windows 10/11の既定がRestricted、Windows Server 2016/2019/2022の既定がRemoteSignedであること、実行ポリシーがスクリプトにのみ影響し対話的なコマンドは常に実行できることについて。  2 3

  3. Microsoft Learn, Set-ExecutionPolicy. 既定スコープがLocalMachineで管理者権限が必要なこと、MachinePolicy/UserPolicyスコープは変更できないこと、グループポリシーを上書きできず競合時にメッセージが表示されること、Unblock-Fileが実行ポリシーを変更せずスクリプトのブロックを解除する例について。  2 3 4 5

  4. Microsoft Learn, Unblock-File. Unblock-Fileがインターネットゾーンを示す値3のZone.Identifier代替データストリームを除去すること、エクスプローラーのプロパティの「許可する」ボタンと同じ操作であること、使用前にファイルと入手元の安全性を確認すべきこと、Get-Item -StreamでZone.Identifier付きファイルを検出できることについて。  2 3 4 5 6

  5. Microsoft Learn, about_Signing. 署名対象のファイル種別、証明機関発行の証明書と自己署名証明書の違い(自己署名はテスト用途限定で他のコンピューターでは実行不可)、署名が# SIG #コメントブロックとして付加されること、PowerShell 7.2より前はASCII/UTF8NoBOM保存が必要だったこと、タイムスタンプサーバーにより証明書の期限後も署名が有効であり続けること、信頼されていない発行元のプロンプトについて。  2 3 4 5 6 7 8 9 10 11 12 13 14 15

  6. Microsoft Learn, Set-AuthenticodeSignature. Authenticode署名の付与、既存署名の置き換え、-TimestampServerパラメーターにより証明書の期限切れ後もスクリプトが失敗しなくなること、Cert:ドライブの-CodeSigningCertパラメーターで秘密鍵つきコード署名証明書を取得する例について。  2 3

  7. Microsoft Learn, New-SelfSignedCertificate. テスト目的の自己署名証明書を作成するコマンドレットであること、-Typeパラメーターでコード署名証明書を指定できること、有効期間の既定が1年であることについて。  2 3

  8. Microsoft Learn, PowerShell security features. 実行ポリシーがスクリプト実行環境のセキュリティを高める複数の機能の一つとして位置づけられ、悪意あるスクリプトの実行防止を助ける安全機能とされていることについて。 

  9. Microsoft Learn, File Streams. NTFSファイルシステムではファイルに書き込まれるデータがストリームとして保持され、1つのファイルが複数のストリームを持てること、既定のデータストリームには名前がないこと、名前付きストリームが「ファイル名:ストリーム名:ストリームの種類」の形式で指定されることについて。 

  10. Microsoft Learn, Active Directory Certificate Services documentation. AD CSが暗号化・デジタル証明書・署名機能のための公開鍵基盤(PKI)を提供すること、証明機関・Web登録・オンライン応答者(OCSP)・ネットワークデバイス登録サービス・証明書登録Webサービス等の役割サービスで構成されること(タイムスタンプ応答サービスは含まれないこと)について。  2

  11. Microsoft Learn, Get-Certificate. 証明書要求を登録サーバーへ送信して応答をインストールするコマンドレットであること、-Templateで証明書テンプレートの名前またはOIDを指定すること、資格情報を指定しない場合はKerberos認証が使われること、-CertStoreLocationで指定できるのはMyストアのみであること、発行済みならStatusがIssued・保留ならPendingになることについて。 

  12. Microsoft Learn, Configure trusted roots and disallowed certificates in Windows. グループポリシー(コンピューターの構成 > ポリシー > Windowsの設定 > セキュリティの設定 > 公開キーのポリシー)から「信頼されたルート証明機関」等のストアへ証明書をインポートして配布する手順について。 

  13. Microsoft Learn, about_Certificate_Provider. PowerShellの証明書プロバイダーが証明書ストアと証明書へのアクセスをCert:ドライブとして提供すること、CurrentUserとLocalMachineという2つのストアの場所と配下の各ストア、証明書が拇印で識別されること、Get-ChildItem等でファイルシステムと同じようにたどれること、-CodeSigningCertパラメーターが拡張キー使用法にコード署名を持つ証明書を取得することについて。  2 3

  14. DigiCert, RFC 3161 compliant Time Stamp Authority server. DigiCertがRFC 3161準拠のタイムスタンプサーバーhttp://timestamp.digicert.comを公開しており、Authenticode署名のタイムスタンプに利用できることについて。 

同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。

このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。

この記事は次のサービスページにつながります。近い入口からご覧ください。

よくある質問

この記事のテーマについて、相談時によくある質問をまとめています。

PowerShellの実行ポリシーはセキュリティ機能ですか?
公式ドキュメントは「実行ポリシーはユーザーの操作を制限するセキュリティシステムではない」と明言しています。スクリプトの内容をコマンドラインに貼り付ければ簡単に迂回できるためです。実行ポリシーの目的は、基本ルールを決めて「うっかり意図しないスクリプトを実行してしまう」事故を防ぐ安全装置であり、攻撃者を止めるセキュリティ境界として設計を組んではいけません。攻撃対策は別のレイヤー(アプリ制御、権限最小化など)で行います。
RemoteSignedとAllSignedはどう違いますか?
RemoteSignedは、インターネット由来(Mark of the Web付き)のスクリプトにのみ信頼された発行元の署名を要求し、ローカルで作成したスクリプトは署名なしで実行できます。AllSignedはローカル作成分も含むすべてのスクリプトと構成ファイルに署名を要求し、未分類の発行元のスクリプトは実行前に確認を求めます。社内で署名運用(コード署名証明書の配布と署名手順)を整備できるならAllSigned、そこまでの体制がないならRemoteSignedが現実的な選択です。
ダウンロードしたスクリプトが「デジタル署名されていません」と言われて実行できないのはなぜですか?
ブラウザーなどでダウンロードしたファイルにはZone.Identifierという代替データストリームが付き、「インターネットから来たファイル」として扱われるためです。実行ポリシーがRemoteSignedの場合、この印が付いた未署名スクリプトは実行がブロックされます。内容を確認して安全だと判断できたら、Unblock-Fileコマンドレットかエクスプローラーのプロパティにある「許可する」でZone.Identifierを除去すれば、実行ポリシーを変えずに実行できます。
社内配布のPowerShellスクリプトはどう運用するのが現実的ですか?
選択肢は大きく2つです。第一はRemoteSigned+ファイルサーバー配置で、署名の仕組みを作らずに始められますが、配布経路によってはMark of the Webが付いて止まることがあり、スクリプトの改変も検知できません。第二はAllSigned+署名運用で、コード署名証明書(社内CA発行が現実的)でSet-AuthenticodeSignatureにより署名し、グループポリシーでポリシーを一元管理します。実行ポリシーの統制と改変検知が効く分、証明書の配布・更新の運用コストがかかります。
タスクスケジューラで -ExecutionPolicy Bypass を指定し続けてよいですか?
動きはしますが推奨しません。まず、グループポリシーで実行ポリシーを管理している環境では、コマンドラインのExecutionPolicy指定はグループポリシーに勝てないため、そもそも効きません。またBypassの常用は「うっかり実行を防ぐ」安全装置を自ら外す運用であり、組織のポリシーと現場の実態が乖離していく原因になります。恒久運用にするなら、グループポリシーか管理者によるSet-ExecutionPolicyで環境側のポリシーを適切に設定し、スクリプト側は署名で信頼を示すのが筋です。
スクリプト署名にタイムスタンプ(-TimestampServer)はなぜ必要ですか?
署名の有効性は原則として署名証明書の有効期限に縛られますが、タイムスタンプがあれば「証明書が有効だった時点で署名された」ことをタイムスタンプサーバーが保証するため、証明書の期限が切れた後もスクリプトを使い続けられます。コード署名証明書の多くは有効期間が1年程度なので、タイムスタンプなしで運用すると毎年「ある日突然、社内の署名済みスクリプトが一斉に動かなくなる」リスクを抱えることになります。署名時には必ず-TimestampServerを指定してください。

著者プロフィール

記事の著者プロフィールページです。

小村 豪

合同会社小村ソフト 代表

Windows ソフト開発、技術相談、不具合調査を中心に、既存資産が残る案件や原因が見えにくい障害調査に強みがあります。

ブログ一覧に戻る