PowerShell CoreのSecureStringについて

PowerShell Core

しばたです。
PowerShell Core 6.2のリリースも間近となり、PowerShell Coreの発表当時から抱えていた大きな問題の一つに解決の見通しが立ったのでお知らせします。

PowerShell Core の SecureString

PowerShell Coreでは開発のかなり初期の段階から非Windows環境においてSecureStringを使った機能が利用できない問題が上がっていました。
わかりやすい例が以下に示すGitHubのIssueで、このIssueが上げられたのが2016年8月とPowerShell Core 6.0のアルファ版の時代になります。

このIssueで例示されている様に、非Windows環境で以下のコードを実行すると例外が発生してしまいます。

$password = Convertto-Securestring -String "PowerShellRocks!" -AsPlainText -Force
ConvertFrom-SecureString $password  

根本原因

根本的な原因は割とシンプルかつ致命的で、SecureStringに関わる暗号化ライブラリがWindows固有のものであり、クロスプラットフォームな.NET Coreで非Windows向けに移植できていないためです。

.NET Core側の事情には疎いのですが、こちらのIssueやAPIレビューを見る限り、相当早い時期からSecureStringを"捨てる"方針を立てているものの互換性の問題もあり苦労している様です。

そして現在は、

  • SecureStringはCoreFxからCoreCLRに実装が移される
  • 非Windows(Unixプラットフォーム)向けの実装を用意するものの暗号化はされない
    • 単純にCharのバイナリを保持するだけになっている模様

といった感じになっている様です。
(このあたりの事情に関して若干自信がないので.NET Coreの経緯に詳しい方のフィードバックいただけると嬉しいです...)

そういった経緯もあり.NET Framework側でも新規にSecureStringを使うことは非推奨とされています。
(別の要因としてそもそもSecureString自体がそこまでセキュアでないというのもあります)

PowerShell Coreの方向性

.NET Core側の事情もありPowerShellとしてSecureStringを扱った処理にどう対処していくかは長い間棚上げされていたのですが、つい先日ConvertFrom-SecureString is broken on LinuxのIssueに次の様なコメントが投稿されました。

Early on, the @PowerShell/powershell-committee discussed introducing a SensitiveString to replace the functional need of SecureString even though both are not secure (the SecureString type would still be needed for backwards compat).
A type (whether "Sensitive" or "Secure" is needed to indicate to PowerShell to prompt without echoing the input so it's used more than just for remoting. As for the original issue of this bug, this has been fixed (you don't get an error anymore), just keep in mind the SecureString is internally in plain text.

そして、コメントに加えて以下のプルリクエストが上げられました。

これらの内容をまとめると、

  1. PowerShellとしてはSecureStringに変わる SensitiveString の様な型の導入を検討していた
  2. SecureStringは互換性維持のために必要
  3. 非Windows環境におけるSecureStringは暗号化されないバイナリ値を前提に処理を修正

という方針になりました。

先述のプルリクがマージされた後の最新ビルドを試してみると下図の様にエラーが解消されます。

ただしこの結果はセキュアではありません。
先述の様に各文字を単純にバイナリ表現しただけですのでご注意ください。

文字 バイナリ値
P 5000
o 6F00
w 7700
e 6500
r 7200
S 5300
h 6800
e 6500
l 6C00
l 6C00
R 5200
o 6F00
c 6300
k 6B00
s 7300
! 2100

Windows環境であればこの様に暗号化された結果の値が返されます。

最後に

ざっと説明しましたがこんな感じです。

プルリクエストが上げられマージされるまでの期間がかなり短いこともあり、まずは互換性維持の部分をPowerShell Core 6.2のリリースに間に合わせたいのだと予想されます。
その後の対応をどうするかについてはPowerShell Core 6.3以降の課題にするつもりなのでしょう。

いずれ公式にアナウンスがされると思います。