web-dev-qa-db-ja.com

SecureStringはC#アプリケーションで実用的ですか?

ここで私の仮定が間違っている場合、私を修正してください、しかし、私が尋ねている理由を説明させてください。

MSDNから取得したSecureString

機密にすべきテキストを表します。テキストは使用時にプライバシー保護のために暗号化され、不要になったらコンピューターのメモリから削除されます。

System.Stringを使用して、実際にメモリに保存する方法とタイミングを制御できるため、SecureStringSystem.Stringを介してパスワードまたは他の個人情報を保存することは完全に理にかなっています

不変であり、不要になったときにプログラムでガベージコレクションのスケジュールを設定することはできません。つまり、インスタンスは作成後は読み取り専用になり、インスタンスがコンピューターのメモリから削除される時期を予測することはできません。そのため、Stringオブジェクトにパスワード、クレジットカード番号、個人データなどの機密情報が含まれている場合、アプリケーションがコンピューターのメモリからデータを削除できないため、使用後に情報が明らかになるリスクがあります。

ただし、GUIアプリケーション(たとえば、sshクライアント)の場合、SecureStringはaSystem.Stringからビルドする必要があります。すべてのテキストコントロールは、基になるデータ型として文字列を使用します

つまり、ユーザーがキーを押すたびに、そこにあった古い文字列が破棄され、パスワードマスクを使用している場合でも、テキストボックス内の値を表す新しい文字列が作成されます。 そして、それらの値のどれかがいつメモリから破棄されるかを制御することはできません

それでは、サーバーにログインします。何だと思う? 認証のために接続を介して文字列を渡す必要がありますSecureStringSystem.String....に変換してみましょう。ヒープに文字列があり、ガベージコレクションを強制的に実行する方法がありません(または、バッファに0を書き込みます)。

私のポイントは:あなたが何をしても、線に沿って、SecureStringgoingであり、System.Stringに変換される、少なくともある時点でヒープ上に存在することを意味します(ガベージコレクションの保証なし)。

私のポイントはそうではありません:ssh接続への文字列の送信を回避する方法があるか、コントロールに文字列を保存することを回避するか(カスタムコントロールを作成します)。この質問では、「ssh connection」を「login form」、「registration form」、「payment form」、「foods-you-would-feed-your-puppy-but-not-your-children form」に置き換えることができます。等.

  • それで、どの時点でSecureStringの使用が実際に実用的になりますか?
  • System.Stringオブジェクトの使用を完全に根絶することは、余分な開発時間の価値がありますか?
  • SecureStringの重要な点は、System.Stringがヒープ上にある時間を単純に短縮する(物理スワップファイルに移動するリスクを減らす)ことですか?
  • 攻撃者がすでにヒープ検査の手段を持っている場合、おそらく(A)はすでにキーストロークを読み取る手段を持っているか、(B)すでに物理的にマシンを持っています...では、SecureStringを使用すると、とにかくデータにアクセスできなくなりますか?
  • これは単なる「あいまいさによるセキュリティ」ですか?

質問をあまりにも厚くしすぎてすみませんが、好奇心がちょうど良くなりました。私の質問のいずれかまたはすべてに自由に答えてください(または、私の仮定が完全に間違っていると教えてください)。 :)

210
Steven Jeffries

SecureStringには実際に非常に実用的な用途があります。

そのようなシナリオを見た回数を知っていますか? (答えは:多く!):

  • パスワードが誤ってログファイルに表示されます。
  • パスワードはどこかで表示されています-GUIが実行中のアプリケーションのコマンドラインを表示すると、コマンドラインはパスワードで構成されていました。 おっと
  • メモリプロファイラーを使用して同僚とソフトウェアのプロファイルを作成します。同僚は、メモリ内のパスワードを確認します。非現実的に聞こえますか?どういたしまして。
  • 私はかつて、例外の場合にローカル変数の「値」をキャプチャできるRedGateソフトウェアを使用していました。これは驚くほど便利です。ただし、誤って「文字列パスワード」を記録することは想像できます。
  • 文字列パスワードを含むクラッシュダンプ。

これらの問題をすべて回避する方法を知っていますか? SecureString。一般的に、そのような愚かな間違いをしないようにします。それをどのように回避しますか?パスワードが管理されていないメモリで暗号化されていることを確認することで、実際の値にアクセスできるのは、自分が何をしているのかが90%わかっているときだけです。

ある意味で、SecureStringは非常に簡単に機能します。

1)すべてが暗号化されています

2)ユーザーがAppendCharを呼び出します

3)UNMANAGED MEMORYのすべてを復号化し、キャラクターを追加します

4)UNMANAGED MEMORYですべてを再度暗号化します。

ユーザーがコンピューターにアクセスできる場合はどうなりますか?ウイルスはすべてのSecureStringsにアクセスできますか?はい。必要なのは、メモリが復号化されるときにRtlEncryptMemoryにフックするだけで、暗号化されていないメモリアドレスの場所を取得し、それを読み出すことができます。出来上がり!実際、SecureStringの使用を常にスキャンし、それを使用してすべてのアクティビティを記録するウイルスを作成できます。私はそれが簡単なタスクになるとは言っていませんが、それは実行できます。ご覧のとおり、システムにユーザー/ウイルスが存在すると、SecureStringの「強力さ」は完全になくなります。

投稿にはいくつかのポイントがあります。確かに、「文字列パスワード」を内部に保持するUIコントロールの一部を使用する場合、実際のSecureStringを使用することはあまり役に立ちません。それでも、それは私が上にリストしたいくつかの愚かさから保護することができます。

また、他の人が指摘したように、WPFは SecurePasswordプロパティを介してSecureStringを内部で使用するPasswordBoxをサポートしています。

一番下の行は;機密データ(パスワード、クレジットカード、..)がある場合は、SecureStringを使用します。これは、C#フレームワークがフォローしているものです。たとえば、NetworkCredentialクラスはパスワードをSecureStringとして保存します。 this を見ると、SecureStringの.NETフレームワークで〜80を超えるさまざまな使用法を確認できます。

SecureStringを文字列に変換する必要がある場合は、多くの場合があります。一部のAPIはそれを予期しているためです。

通常の問題は次のいずれかです。

  1. APIはGENERICです。機密データがあることを知りません。
  2. このAPIは、機密データを処理していることを認識しており、「文字列」を使用しています-それは単に悪い設計です。

良い点を挙げました:SecureStringstringに変換されるとどうなりますか?これは、最初の点が原因でのみ発生します。例えば。 APIは、それが機密データであることを知りません。私は個人的にそれが起こっているのを見たことはありません。 SecureStringから文字列を取得するのはそれほど簡単ではありません。

単純な理由で単純ではありません;あなたが述べたように、ユーザーがSecureStringを文字列に変換できるようにすることは決して意図されていませんでした:GCが起動します。あなたがそれをしているのを見たら、あなたは戻って自分自身に尋ねる必要があります:これ、なぜ?

私が見た興味深いケースが1つあります。つまり、WinApi関数LogonUserはLPTSTRをパスワードとして使用するため、SecureStringToGlobalAllocUnicodeを呼び出す必要があります。これは基本的に、管理されていないメモリにある暗号化されていないパスワードを提供します。完了したらすぐにそれを取り除く必要があります。

// Marshal the SecureString to unmanaged memory.
IntPtr rawPassword = Marshal.SecureStringToGlobalAllocUnicode(password);
try
{
   //...snip...
}
finally 
{
   // Zero-out and free the unmanaged string reference.
   Marshal.ZeroFreeGlobalAllocUnicode(rawPassword);
}

ToEncryptedString(__SERVER__PUBLIC_KEY)などの拡張メソッドを使用して、SecureStringクラスをいつでも拡張できます。これは、サーバーの公開キーを使用して暗号化されるstringSecureStringインスタンスを提供します。サーバーのみが復号化できます。問題解決:ガベージコレクションは、マネージメモリで公開しないため、「元の」文字列を参照しません。これはまさに PSRemotingCryptoHelperEncryptSecureStringCore(SecureString secureString))で行われていることです。

そして、非常に関連性の高いものとして:Mono SecureStringはまったく暗号化しません。 ..wait for .. "nunit test breakage" であるため、実装はコメントアウトされています。

SecureStringはどこでもサポートされていません。プラットフォーム/アーキテクチャがSecureStringをサポートしていない場合、例外が発生します。ドキュメントでサポートされているプラ​​ットフォームのリストがあります。

226

あなたの仮定の問題はほとんどありません。

まず、SecureStringクラスにはStringコンストラクターがありません。オブジェクトを作成するには、オブジェクトを割り当ててから文字を追加します。

GUIまたはコンソールの場合、押された各キーを安全な文字列に非常に簡単に渡すことができます。

このクラスは、誤って保存されている値にアクセスできないように設計されています。つまり、stringを直接パスワードとして取得することはできません。

そのため、たとえばWebを介して認証するためにこれを使用するには、安全な適切なクラスを使用する必要があります。

.NETフレームワークには、SecureStringを使用できるクラスがいくつかあります

  • WPFのPasswordBoxコントロールは、パスワードをSecureStringとして内部的に保持します。
  • System.Diagnostics.ProcessInfoのPasswordプロパティはSecureStringです。
  • X509Certificate2のコンストラクターは、パスワードにSecureStringを使用します。

(詳細)

結論として、SecureStringクラスは便利ですが、開発者の注意が必要です。

これらはすべて、例とともに、MSDNのドキュメント SecureString で詳しく説明されています。

SecureStringは次の場合に便利です。

  • 文字ごとに(たとえば、コンソール入力から)ビルドするか、アンマネージAPIから取得します

  • アンマネージAPI(SecureStringToBSTR)に渡すことで使用します。

マネージストリングに変換した場合、その目的は無効になります。

UPDATEコメントへの応答

...またはあなたが言ったBSTR、これ以上安全ではないようです

BSTRに変換された後、BSTRを消費するアンマネージコンポーネントはメモリをゼロにできます。アンマネージメモリは、この方法でリセットできるという意味でより安全です。

ただし、.NET FrameworkにはSecureStringをサポートするAPIはほとんどないため、今日では非常に限られた価値しかないと言っても過言ではありません。

私が見る主なユースケースは、ユーザーに非常に機密性の高いコードまたはパスワードの入力を要求するクライアントアプリケーションです。ユーザー入力を文字ごとに使用してSecureStringを構築し、これをアンマネージAPIに渡すことができます。これにより、使用後に受信したBSTRがゼロになります。後続のメモリダンプには、機密性の高い文字列は含まれません。

サーバーアプリケーションでは、どこで役立つかわかりにくいです。

更新2

SecureStringを受け入れる.NET APIの1つの例は、 X509Certificateクラスのこのコンストラクター です。 ILSpyなどでスペルチェックした場合、SecureStringは内部でアンマネージバッファー(Marshal.SecureStringToGlobalAllocUnicode)に変換され、(Marshal.ZeroFreeGlobalAllocUnicode)で終了するとゼロになります。

10
Joe

正しく識別できたように、 SecureString は、stringよりも特定の利点を1つ提供します:確定的消去。この事実には2つの問題があります。

  1. 他の人が述べたように、そしてあなたが自分で気づいたように、これだけでは十分ではありません。 SecureStringを使用する目的に反することなく、プロセスのすべてのステップ(入力の取得、文字列の構築、使用、削除、輸送など)が確実に行われるようにする必要があります。つまり、GC管理の不変のstringや、機密情報を保存するその他のバッファーを作成しないように注意する必要があります(またはthatを追跡する必要があります)まあ)。実際、多くのAPIはstringではなくSecureStringを操作する方法しか提供していないため、これを達成するのは必ずしも容易ではありません。そして、あなたがすべてを正しく管理していても...
  2. SecureStringは、非常に特殊な種類の攻撃から保護します(一部の攻撃では、それほど信頼できません)。たとえば、SecureStringを使用すると、攻撃者がプロセスのメモリをダンプして機密情報を正常に抽出できる時間枠を縮小できます(これも正しく指摘したとおりです)メモリのスナップショットを取得することは、セキュリティとはみなされません。

それで、いつそれを使うべきですか? SecureStringを必要に応じて操作できるものを使用している場合にのみ、特定の状況でのみ安全であることに注意する必要があります。

この点に対処したいと思います。

攻撃者がすでにヒープ検査の手段を持っている場合、おそらく(A)キーストロークを読み取る手段をすでに持っているか、(B)すでに物理的にマシンを持っている ... SecureStringは、とにかくデータにアクセスできないようにしますか?

攻撃者はコンピュータとアプリケーションへの完全なアクセス権を持たない場合がありますが、プロセスのメモリの一部にアクセスする手段を持つことができます。通常、特別に構築された入力によってアプリケーションがメモリの一部を公開または上書きできる場合、バッファオーバーランなどのバグが原因です。

ハートブリードリークメモリ

Heartbleedを例にとってみましょう。特別に構築されたリクエストにより、コードがプロセスのメモリのランダムな部分を攻撃者にさらす可能性があります。攻撃者はメモリからSSL証明書を抽出できますが、必要なのは不正なリクエストを使用することだけです。

マネージコードの世界では、バッファーオーバーランが問題になることはほとんどありません。また、WinFormsの場合、データはすでに安全でない方法で保存されており、ユーザーは何もできません。これにより、SecureStringによる保護はほとんど役に立たなくなります。

ただし、GUI canSecureStringを使用するようにプログラムできます。そのような場合、メモリ内のパスワードの可用性のウィンドウを減らすことは価値があります。たとえば、WPFの PasswordBox.SecurePasswordSecureString型です。

3
Athari

以下のテキストはHP Fortify静的コードアナライザーからコピーされます

Abstract: PassGenerator.csのメソッドPassString()は、機密データを安全でない方法(つまり、文字列)で保存し、ヒープを検査してデータを抽出できるようにします。

説明:メモリに格納されている機密データ(パスワード、社会保障番号、クレジットカード番号など)が管理対象のStringオブジェクトに格納されている場合、漏洩する可能性があります。文字列オブジェクトは固定されていないため、ガベージコレクターはこれらのオブジェクトを自由に再配置し、いくつかのコピーをメモリに残すことができます。これらのオブジェクトはデフォルトでは暗号化されていないため、プロセスのメモリを読み取れる人はだれでも内容を見ることができます。さらに、プロセスのメモリがディスクにスワップアウトされると、暗号化されていない文字列の内容がスワップファイルに書き込まれます。最後に、Stringオブジェクトは不変であるため、メモリからStringの値を削除できるのは、CLRガベージコレクターのみです。ガベージコレクターは、CLRのメモリが不足していない限り実行する必要がないため、ガベージコレクションがいつ実行されるかについての保証はありません。アプリケーションがクラッシュした場合、アプリケーションのメモリダンプから機密データが明らかになる可能性があります。

推奨事項:文字列などのオブジェクトに機密データを保存する代わりに、SecureStringオブジェクトに保存します。各オブジェクトは、常にその内容を暗号化された形式でメモリに保存します。

2
vikrantx

しばらく前に、Javaクレジットカード支払いゲートウェイに対してc#インターフェイスを作成する必要があり、互換性のある安全な通信キー暗号化が必要でした。 Java実装はかなり具体的であったため、指定された方法で保護されたデータを操作する必要がありました。

この設計は非常に使いやすく、SecureStringを使用するよりも簡単であることがわかりました。使いたい人にとっては…気軽に、法的な制限はありません:-)。これらのクラスは内部であるため、公開する必要がある場合があります。

namespace Cardinity.Infrastructure
{
    using System.Security.Cryptography;
    using System;
    enum EncryptionMethods
    {
        None=0,
        HMACSHA1,
        HMACSHA256,
        HMACSHA384,
        HMACSHA512,
        HMACMD5
    }


internal class Protected
{
    private  Byte[] salt = Guid.NewGuid().ToByteArray();

    protected byte[] Protect(byte[] data)
    {
        try
        {
            return ProtectedData.Protect(data, salt, DataProtectionScope.CurrentUser);
        }
        catch (CryptographicException)//no reason for hackers to know it failed
        {
#if DEBUG
            throw;
#else
            return null;
#endif
        }
    }

    protected byte[] Unprotect(byte[] data)
    {
        try
        {
            return ProtectedData.Unprotect(data, salt, DataProtectionScope.CurrentUser);
        }
        catch (CryptographicException)//no reason for hackers to know it failed
        {
#if DEBUG
            throw;
#else
            return null;
#endif
        }
    }
}


    internal class SecretKeySpec:Protected,IDisposable
    {
        readonly EncryptionMethods _method;

        private byte[] _secretKey;
        public SecretKeySpec(byte[] secretKey, EncryptionMethods encryptionMethod)
        {
            _secretKey = Protect(secretKey);
            _method = encryptionMethod;
        }

        public EncryptionMethods Method => _method;
        public byte[] SecretKey => Unprotect( _secretKey);

        public void Dispose()
        {
            if (_secretKey == null)
                return;
            //overwrite array memory
            for (int i = 0; i < _secretKey.Length; i++)
            {
                _secretKey[i] = 0;
            }

            //set-null
            _secretKey = null;
        }
        ~SecretKeySpec()
        {
            Dispose();
        }
    }

    internal class Mac : Protected,IDisposable
    {
        byte[] rawHmac;
        HMAC mac;
        public Mac(SecretKeySpec key, string data)
        {

            switch (key.Method)
            {
                case EncryptionMethods.HMACMD5:
                    mac = new HMACMD5(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA512:
                    mac = new HMACSHA512(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA384:
                    mac = new HMACSHA384(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA256:
                    mac = new HMACSHA256(key.SecretKey);

                break;
                case EncryptionMethods.HMACSHA1:
                    mac = new HMACSHA1(key.SecretKey);
                    break;

                default:                    
                    throw new NotSupportedException("not supported HMAC");
            }
            rawHmac = Protect( mac.ComputeHash(Cardinity.ENCODING.GetBytes(data)));            

        }

        public string AsBase64()
        {
            return System.Convert.ToBase64String(Unprotect(rawHmac));
        }

        public void Dispose()
        {
            if (rawHmac != null)
            {
                //overwrite memory address
                for (int i = 0; i < rawHmac.Length; i++)
                {
                    rawHmac[i] = 0;
                }

                //release memory now
                rawHmac = null;

            }
            mac?.Dispose();
            mac = null;

        }
        ~Mac()
        {
            Dispose();
        }
    }
}

Microsoftは、新しいコードにSecureStringを使用することを推奨していません。

SecureString Class のドキュメントから:

重要

新規開発にSecureStringクラスを使用することはお勧めしません。詳細については、 SecureStringを使用しないでください を参照してください。

推奨されるもの:

新しいコードにSecureStringを使用しないでください。コードを.NET Coreに移植するときは、配列の内容がメモリ内で暗号化されていないことを考慮してください。

資格情報を扱う一般的な方法は、資格情報を回避し、代わりに証明書やWindows認証など、他の認証方法に依存することです。 GitHubで。

0
SᴇM