PowerShellの実行ポリシーについて調査（その設定で本当に大丈夫ですか？）

# PowerShellの実行ポリシー(ExecutionPolicy)
PowerShellには実行ポリシーというものがあって、何も設定していないと`.ps1`のスクリプトファイルの実行がブロックされます。

```powershell
.\poetry-template.ps1 : このシステムではスクリプトの実行が無効になっているため、
ファイル poetry-template.ps1 を読み込むことができません。詳細に
ついては、「about_Execution_Policies」(https://go.microsoft.com/fwlink/?LinkID=1
35170) を参照してください。
発生場所 行:1 文字:1
+ .\poetry-template.ps1 hoge
+ ~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : セキュリティ エラー: (: ) []、PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess
```

https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.3

実行ポリシーをどうするかは悩みどころです。

```powershell
Set-ExecutionPolicy RemoteSigned
```
って設定すればいいよ、と書いてある記事も多いですがちょっと待った！
おそらくそれは適切な設定でない場合が多いです。

本記事では、実行ポリシーについて調べたり実際に動かして確かめてみたりしたことを書きます。
ただし、セキュリティに関わることなので設定変更はご慎重に。
本記事のターゲットは「個人PCで趣味程度に開発している一般Windowsユーザー」です。
企業PCの場合は(基本的に)設定いじらないか上司や情シスに確認を。
個人PCでも自己責任で！


# 結論から

※個人のPCであるという前提の上で

一回のみの実行なら、
```powershell
powershell -ExecutionPolicy RemoteSigned .\test.ps1
```

１スコープのみのスクリプト利用であれば、
```powershell
Set-ExecutionPolicy RemoteSigned Process
# 現在のスコープでのみ(今開いているpowershellを閉じるまで)実行が有効になる
```

頻繁にpowershellスクリプトを利用する場合は、
```powershell
Set-ExecutionPolicy RemoteSigned CurrentUser
# 現在のユーザーで実行が有効になる
```

の設定で良い場合が多そう。

ただし、実行ポリシー変更によるセキュリティリスクはしっかり理解しておくこと。
利用用途によって適切な設定は異なります。
少なくとも公式ドキュメントは読みましょう。


# 実行ポリシーの種類

改めて公式ドキュメント。

https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.3

実行ポリシーは厳しい順に `Restricted`(デフォルト), `AllSigned`, `RemoteSigned`, `Unrestricted`, `Bypass` です。

こちらのQiita記事の表がわかりやすいですね。

https://qiita.com/tomoko523/items/df8e384d32a377381ef9

> |引数| ローカル| リモート |
> |:-----------|:------------:|:------------:|
> | Restricted |×|×|←でデフォルト
> | AllSigned | 署名付きのみ〇|署名付きのみ〇|
> | RemoteSigned |〇|署名付きのみ〇|
> | Unrestricted |〇|〇|




スコープは狭い順に、`Process`(現在のセッションのみ), `CurrentUser`(現在のユーザーのみ),`LocalMachine`(現在のコンピュータすべて) です。（MachinePolicyとUserPolicyはグループポリシー関係ですが、本記事では扱いません。）
狭いスコープの実行ポリシーが優先されます。

できるだけ実行ポリシーが厳しい＆狭いスコープの方がセキュリティ的には良いですよね。
利便性とセキュリティはトレードオフなところがあるので、どのレベルにしておくかはご自身の環境に合わせた設定が必要です。


# 確認方法・設定方法

`Get-ExecutionPolicy`で現在のポリシーを確認できます。
```powershell
> Get-ExecutionPolicy
RemoteSigned
```

`Get-ExecutionPolicy -List`でスコープごとのポリシーを確認できます。
```powershell
> Get-ExecutionPolicy -List

        Scope ExecutionPolicy
        ----- ---------------
MachinePolicy       Undefined
   UserPolicy       Undefined
      Process       Undefined
  CurrentUser       RemoteSigned
 LocalMachine       Undefined
```

上から見ていって最初にUndefinedではないポリシーが適用されます。（↑の例はRemoteSigned）
すべてUndefinedの場合は(普通のwindowsPCなら)Restrictedになります。

次にポリシーの設定方法です。
`CurrentUser`スコープで`RemoteSigned`にしたい場合は以下の通り。
```powershell
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
```
第一引数、第二引数の順を守っていればオプション名の省略も可能です。
```powershell
Set-ExecutionPolicy RemoteSigned CurrentUser
```

スコープのデフォルトは`LocalMachine`です。
そして`LocalMachine`スコープのポリシー変更には管理者権限が必要です。
セキュリティ的には緩いポリシーはできるだけ狭いスコープに抑えたいので、必要ないのであれば緩いポリシーは`LocalMachine`ではなく`CurrentUser`で設定した方がよいですね。

全ユーザーに対して実行ポリシーを適用させたい意図があるなら`LocalMachine`でもいいですが、おそらく現在のユーザーに設定を適用させたい場合がほとんどだと思います。

つまり、少なくとも
```powershell:（管理者権限）
Set-ExecutionPolicy RemoteSigned
```
はやめて、
```powershell
Set-ExecutionPolicy RemoteSigned CurrentUser
```
にしましょう。（`Set-ExecutionPolicy RemoteSigned CurrentUser`の是非はこれ以降の章で検証）


# Python公式ドキュメントに実行ポリシーに関する記述あり
セキュリティに関わる重要な項目。
できれば信頼できる機関がどう判断しているのか参考にしたいところです。

Python公式ドキュメントでpowershellの実行ポリシーに関する記述を見つけました。
venvの項目です。（venv: 公式が提供するPythonの仮想環境）
venvで仮想環境をアクティブにするとき、powershellでは`Activate.ps1`を実行する必要があります。
なので、何かしら実行ポリシーを変更する必要があるのです。

https://docs.python.org/3/library/venv.html?highlight=executionpolicy

> Note On Microsoft Windows, it may be required to enable the Activate.ps1 script by setting the execution policy for the user. You can do this by issuing the following PowerShell command:
PS C:> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
See About Execution Policies for more information.

Windowsユーザーは実行ポリシーを`CurrentUser`スコープで`RemoteSigned`にするよう書いてありますね！

`RemoteSigned`よりも実行ポリシーが厳しい`AllSigned`については以下の記事が非常に参考になります。

https://qiita.com/earthdiver1/items/b3f529c81f5892972a72

ただ、venvの用途を考えると`AllSigned`や`Process`スコープで運用するのは、(できなくはないが)少し厳しいでしょう。
venvを使う場合は`CurrentUser`スコープの`RemoteSigned`で設定するのは適切だと思います。

大手がそう言っているからと言ってそれが正しいとは限りませんが、何の判例がないよりはマシです。
「頻繁に新しいps1ファイルを実行する必要がある場合、`Set-ExecutionPolicy RemoteSigned CurrentUser`と設定するのはある程度妥当かもしれない」と一旦考えてみます。

その上で、`RemoteSigned`で何が防げて`Restricted`と比べて何が防げなくなったのかはきちんと確認しておきましょう。


# RemoteSignedについて調査
公式ドキュメントでは`RemoteSigned`について以下のように書かれています。
>RemoteSigned
Windows サーバー コンピューターの既定の実行ポリシー。
スクリプトを実行できます。
電子メールやインスタント メッセージング プログラムを含むインターネットからダウンロードされたスクリプトと構成ファイルに、信頼できる発行元からのデジタル署名が必要です。
ローカル コンピューターで記述され、インターネットからダウンロードされていないスクリプトにデジタル署名は必要ありません。
コマンドレットを使用するなどして、スクリプトのブロックが解除されている場合は、インターネットからダウンロードされ、署名されていないスクリプトを Unblock-File 実行します。
インターネット以外のソースから署名されていないスクリプトを実行し、悪意のある可能性がある署名付きスクリプトを実行するリスクがあります。
https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.3#remotesigned

要約すると、
* 署名付きは実行可能
* 署名なしでローカル作成は実行可能
* 署名なしでインターネットからダウンロードされたものは実行できない

ということになります。

ほんとに`RemoteSigned`ではインターネットからダウンロードされたものは実行できないのか試してみました。

## ローカルで作成した ps1 を実行してみる
まずローカル作成のps1ファイルを実行してみましょう。
```powershell:test.ps1
Write-Output "hello world!"
```
```
> .\test.ps1
hello world!
```

これは普通に実行できるはずですね。


## ps1 を git clone して実行してみる
GitHubにps1ファイルが入ったリポジトリを作成。

https://github.com/torippy1024/project-from-template-script/blob/master/poetry-template.ps1

これをcloneして実行ポリシー`RemoteSigned`で実行してみると…
エラーが発生することなく実行できてしまいました。
git clone はインターネットからダウンロードした判定にならないんですね。


## ps1 をダウンロードして実行してみる

今度はcloneではなく、Download ZIP でローカルに落として解凍してps1ファイルを実行してみたところ…
今後はエラーが出ました。いわゆるダウンロードで落としたものはちゃんとブロックしてくれてそうですね。
```powershell
.\poetry-template.ps1 : ファイル C:\Users\hoge\Downloads\project-from-template-script-master\project-from-template-scr
ipt-master\poetry-template.ps1 を読み込めません。ファイル C:\Users\hoge\Downloads\project-from-template-script-master\
project-from-template-script-master\poetry-template.ps1 はデジタル署名されていません。このスクリプトは現在のシステムで
は実行できません。スクリプトの実行および実行ポリシーの設定の詳細については、「about_Execution_Policies」(https://go.mic
rosoft.com/fwlink/?LinkID=135170) を参照してください。
発生場所 行:1 文字:1
+ .\poetry-template.ps1
+ ~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : セキュリティ エラー: (: ) []、PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess
```


もしこのインターネットからダウンロードされたファイルのブロックを解除したい場合は、Unblock-Fileというコマンドを使うと良いようです。
ブロックを解除すると、ローカルで作成したファイルと同じ判定になります。

https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.utility/unblock-file?view=powershell-7.3

zipで落としたps1ファイルも
```powershell
Unblock-File poetry-template.ps1
```
でブロック解除すると、`RemoteSigned`の状態でも実行できるようになりました。

なるほど…。


## メールから ps1 をダウンロード？
そういえば公式ドキュメントには、
> 電子メールや…からダウンロードされたスクリプト…

と書いてあったので、自分宛てにps1ファイルを送ってそれをダウンロードして確かめてみようと思ったのですが…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072823/7ada0e1f-1078-7da2-8582-e2f197a7af58.png)

> セキュリティ上の理由によりブロックされました。

> Gmail では、ウイルスや不正なソフトウェアからユーザーのアカウントを保護するため、次のようなファイルは添付できません。
特定のファイル形式。これには、圧縮されている場合（.gz や .bz2 など）や、アーカイブに格納されている場合（.zip や .tgz など）も含まれます

まず送ることができませんでした。

Gmailではps1ファイルを普通には送ることができないみたいですね。
でもそれはGmailがちゃんとしているからであって、他のところからは送信できたりすると思うので注意。
今回検証しませんでしたが、おそらく電子メールからダウンロードしたps1ファイルも`RemoteSigned`でブロックしてくれるんだと思います。

## GUIで実行してみる
今までpowershellからCUIで実行しようとしていましたが、GUI(つまりダブルクリックなど)で実行しようとしたらどうなるでしょうか？
(windows11 で確認したので windows10 ではちょっと違う挙動になるかもしれません)

ダブルクリックするとまず警告が出ました。
![スクリーンショット 2023-02-18 192228.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072823/3f555c70-826f-9e75-e575-c1ac3ad8ba20.png)

「開く」を押すとスクリプトが実行できるのかな？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072823/8a6f8915-1265-43e7-8a8e-1e8a7eb980bb.png)

メモ帳が開きましたね。
ps1ダブルクリックはデフォルトではメモ帳で開くだけみたいです。

右クリックメニューに「PowerShellで実行」があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072823/9ea077bf-2d51-4c33-4d56-1031e11a4d4c.png)

こちらは予想通りスクリプト自体を実行するものですが、実行できるかどうかは実行ポリシーに準じます。
実行ポリシーにそぐわない場合は、一瞬powershellが立ち上がるのですが中身が実行されずにそのまま終了します。


# そもそも`Restricted`ってどこまで防げるのか？
`Restricted`は一番厳しい実行ポリシーであり、すべてのps1ファイルが実行できなくなると公式ドキュメントには書いてあります。
前の章の`RemoteSigned`で実行できた、「ローカルで作成したps1ファイル」も「git clone したps1ファイル」も`Restricted`で試してみると確かに実行できませんでした。

では、`Restricted`にしていたら絶対にps1ファイルを実行できないのでしょうか？
答えは No です。

## 実は結構簡単に回避できちゃう
こちらのQiita記事には、ショートカットやバッチファイルでps1ファイルを実行する方法が書いてあります。

https://qiita.com/tomoko523/items/df8e384d32a377381ef9

簡単に説明すると、ショートカットやバッチファイルで一時的に実行ポリシーを緩くした状態でpowershellを立ち上げてps1ファイルを実行している感じです。

同じような手順で、例えばPythonからもps1ファイルを実行可能です。

ここに危ないps1ファイルがあるとします。
```powershell:danger.ps1
Write-Output "Your computer has been infected with a virus!"
```
これを`Restricted`のときに普通に実行しようとするとエラーが出ます。
```powershell
> Get-ExecutionPolicy
Restricted

> .\danger.ps1                     .\danger.ps1 : このシステムではスクリプトの実行が無効になっている
ため、ファイル C:\Users\hoge\Downloads\temp\danger.ps1 を読み込む 
ことができません。詳細については、「about_Execution_Policies」(htt 
ps://go.microsoft.com/fwlink/?LinkID=135170) を参照してください。  
発生場所 行:1 文字:1
+ .\danger.ps1
+ ~~~~~~~~~~~~
    + CategoryInfo          : セキュリティ エラー: (: ) []、PSSecu 
rityExcep    tion
    + FullyQualifiedErrorId : UnauthorizedAccess
```

次はPythonの`os.system()`で実行しようとします。
```python:main.py
import os

os.system("powershell .\danger.ps1")

```
```powershell
> Get-ExecutionPolicy
Restricted

> python .\main.py
.\danger.ps1 : File C:\Users\hoge\Downloads\temp\danger.ps1 canno
t be loaded because running scripts is disabled on this system. Fo
r more information, see about_Execution_Policies at https:/go.micr
osoft.com/fwlink/?LinkID=135170.
At line:1 char:1
+ .\danger.ps1
+ ~~~~~~~~~~~~
    + CategoryInfo          : SecurityError: (:) [], PSSecurityEx  
   ception
    + FullyQualifiedErrorId : UnauthorizedAccess
```
これもブロックされました。
ここまではOKですね。


では先ほどのQiita記事のように実行ポリシーオプションをつけてみて実行します。
```python:main.py
import os

os.system("powershell -ExecutionPolicy RemoteSigned .\danger.ps1")

```
```powershell
> Get-ExecutionPolicy
Restricted

> python .\main.py
Your computer has been infected with a virus!
```
実行できてしまいました！

今回はPythonのコードは一行しかないのでps1ファイルを実行しようとしていることは丸わかりですし、danger.ps1なんて名前のファイルは明らかに実行してはいけないとわかります。
しかし、複数ファイルかつ何百行のPythonプログラムに１行だけ入っていたらどうでしょうか？
ps1ファイルが複雑にネストされた場所に置かれていたらどうでしょうか？
上記の通り実行ポリシーオプションをつけてなければブロックできますが、悪意を持って実行させようとしているなら当然ただ十文字程度加えるだけの実行ポリシーオプションをつけないことはないでしょう。

それに、サイバー攻撃の手口はそんな単純なものばかりではなく、色々な手法を使って対策を回避してきます。powershellを使ったものだとファイルレスマルウェアとか。

https://msrc.microsoft.com/blog/2018/02/now-you-see-me-exposing-fileless-malware/

https://eset-info.canon-its.jp/malware_info/special/detail/210128.html

要するに、実行ポリシーの設定だけではpowershellに関するセキュリティリスクを完全に排除することはできないというわけです。
きちんと対策・運用するならそれ以上の措置が必要でしょう。
一般社団法人ソフトウェア協会のセキュリティ委員会が母体の「Software ISAC」がPowerShellの悪用に対する措置等について詳しく書いてくれています。

https://www.softwareisac.jp/ipa/index.php?%E3%83%BBPowerShell%E3%81%AE%E6%82%AA%E7%94%A8

個人PCでもこのサイト通りにきっちり管理しても良いとは思います。
ただ、個人と企業(組織)では抱えてるセキュリティリスクの大きさに差があるでしょう。その上、一般ユーザーが上記内容を理解して設定・運用するのは少し難しいでしょう。~~（企業でもここまできっちり管理しているのは全体の何%くらいなんだろうか…）~~


## 結局実行ポリシーは意味あるの？

もちろんあります。
確かに実行ポリシーだけで完全な防御は無理です。
しかし、「多角的に対策をとる」というのがセキュリティの基本です。

本記事で紹介した範囲でも、
* Gmailでのps1ファイル送信を禁止することで、そもそも悪意あるスクリプトの流布を防ぐ
* 実行ポリシーによってよくわからないまま実行することを防ぐ
* ダブルクリックで実行できないようにすることで、１クッション置かせる
    * 特に拡張子表示設定をオフにしてると、`資料.docx.ps1`の表示が`資料.docx`になるのでスクリプトファイルと知らずに引っかかってダブルクリックしてしまう人が多そうです

といったように複数の対策が取られていますよね。

実行ポリシーによってどれだけ自分のPCが守られるかは本人のセキュリティ意識や使い方などによりますが、少なくともこの記事を見に来た人の大多数が、ps1を意識的にか無意識的に実行しようとして実行ポリシーの警告・エラーを受けて来ているんだと思います。
すべての脅威から完全に守ることはできませんが、一部の脅威をブロックすることができ、そして実行ポリシーが高いほどブロックできる割合も高くなります。


# 改めて、どうしたらいい？
※個人のPCであるという前提の上で

一回のみの実行なら、
```powershell
powershell -ExecutionPolicy RemoteSigned .\test.ps1
```

１スコープのみのスクリプト利用であれば、
```powershell
Set-ExecutionPolicy RemoteSigned Process
# 現在のスコープでのみ(今開いているpowershellを閉じるまで)実行が有効になる
```

頻繁にpowershellスクリプトを利用する場合は、
```powershell
Set-ExecutionPolicy RemoteSigned CurrentUser
# 現在のユーザーで実行が有効になる
```

で良い場合が多いのではないかなと思います。

Pythonの公式ドキュメントでも`RemoteSigned`が使われているように、
色々な点を考慮すると、個人PCであれば`RemoteSigned`が利便性とセキュリティのバランスの良いのではという判断です。

もちろん実行ポリシーを高く保つに越したことはありません。
ご自身の利用用途等に応じて適切な実行ポリシーを設定しましょう。

もう少し実行ポリシーが高い`AllSigned`を保ちたい場合はこちら記事が参考になりますし、

https://qiita.com/earthdiver1/items/b3f529c81f5892972a72

ガチガチに固めたい場合はこちらの記事を参考にすると良いと思います。

https://www.softwareisac.jp/ipa/index.php?%E3%83%BBPowerShell%E3%81%AE%E6%82%AA%E7%94%A8


適切な実行ポリシーを設定した上で、でもそれだけではpowershellリスクを完全に排除できないことを理解し、基本的なセキュリティ対策を取りましょう。

powershell関わらず下の項目はごくごく一般的なセキュリティ対策です。
* 信頼できないサイトからダウンロードしない
* 信頼できないメールのURLや添付ファイルを開かない
* 信頼できないファイルを実行しない
* ウイルス対策ソフトを導入する
* ソフトウェアを最新の状態に保つ


# 最後に
powershellの実行ポリシーについて、調べたりしたことをまとめました。
「とりあえず`Set-ExecutionPolicy RemoteSigned`や`Set-ExecutionPolicy RemoteSigned CurrentUser`にしとけば良い」みたいな記事はたくさんあるけど、実際にセキュリティリスク的にどうなのかなどについて書いてある記事がなかなかなかったので本記事を書きましたが、もし誰かの参考になれば幸いです。

また、私自身全然セキュリティの職というわけでもなければセキュリティ関連を専攻しているわけでもないので、もし間違った記述やよろしくない記述がありましたら、コメントで教えていただけると助かります。