2013-06-17 7 views
14

リフレクションを使用してStringをスクラブすると、パスワードとしてchar[]を使用すると安全ですか?リフレクションを使用して安全に文字列を使用してガベージコレクションの前に内容をスクラブする

char[]をパスワードの保存/渡しに使用することが一般的にはベストプラクティスと考えられます。できるだけ早くその内容をゼロにすることができます。ガベージコレクションの前には、メモリは再使用され(すべてのトレースを消去します)、メモリ攻撃の時間枠を制限します。

はしかし、char[]Stringほど便利ではありませんので、必要に応じて、1は「スクラブ」Stringは、これchar[]と同様に安全Stringを作ることができれば、それは便利だろう。

以下は、反射を使用してStringのフィールドをゼロにする方法です。

この方法は「OK」ですか、Stringをパスワードとしてchar[]と安全にするという目標を達成していますか?

String str = "password"; 
scrub(str); 
System.out.println('"' + str + '"'); 

出力:

"" 

注:あなたはパスワードがString定数でないと仮定することができるので、このメソッドを呼び出すと、有害なを持っていません。ここ

public static void scrub(String str) throws NoSuchFieldException, IllegalAccessException { 
    Field valueField = String.class.getDeclaredField("value"); 
    Field offsetField = String.class.getDeclaredField("offset"); 
    Field countField = String.class.getDeclaredField("count"); 
    Field hashField = String.class.getDeclaredField("hash"); 
    valueField.setAccessible(true); 
    offsetField.setAccessible(true); 
    countField.setAccessible(true); 
    hashField.setAccessible(true); 
    char[] value = (char[]) valueField.get(str); 
    // overwrite the relevant array contents with null chars 
    Arrays.fill(value, offsetField.getInt(str), countField.getInt(str), '\0'); 
    countField.set(str, 0); // scrub password length too 
    hashField.set(str, 0); // the hash could be used to crack a password 
    valueField.setAccessible(false); 
    offsetField.setAccessible(false); 
    countField.setAccessible(false); 
    hashField.setAccessible(false); 
} 

は、簡単なテストですinterned Stringsに対する効果。

また、私はこのメソッドを簡単にするためにかなり「生の」状態にしておきます。私がそれを使用する場合、例外をスロー(try/catch/ignoring)し、リファクタリングを繰り返すコードを宣言しません。

+0

文字をゼロにするようですが、JVMの実装にはそれほど依存しません。 – fge

+0

メモリスヌーピング?それはファンタジー攻撃です。 NSAがネットワークトラフィックをスヌーピングするような、より現実的なものを心配しないでください。 – ZhongYu

+0

「未定義の動作」を私に叫ぶ。龍、velociraptorsとブラックホールとの幸運:) – CodesInChaos

答えて

7

は、2つの潜在的な安全上の懸念があります。

  1. 他の文字列とその補助配列を共有することがあり文字列。例えばStringがより大きいStringsubstringを呼び出すことによって作成された場合。したがって、value配列全体をゼロにすると、のように、パスワードを含んでいない他の文字列の状態を上書きできます。

    パスワードストリングで使用されるバッキングアレイの部分のみをゼロにすることです。

  2. JLS(17.5.3)は、リフレクションを使用してfinal変数を変更した場合の影響は未定義であることを警告しています。

    ただし、これのコンテキストはJavaメモリモデルであり、コンパイラがfinalの変数を積極的にキャッシュできるという事実です。この場合:

    • あなたは文字列がスレッド制限されることを期待する、と

    • あなたは再びそれらの変数のいずれかを使用してはいけません。

私はモジュロvalueのオーバー積極的なゼロ化を固定...これらのいずれかが本当問題であることを期待していません。


しかし実際のところ、Velociraptorsです。 :-)


私はあなたが実際にこのようなパスワードをザッピングすることに戸惑います。あなたがそれについて考えるとき、あなたが守っているのは、誰かがプロセスメモリやコアダンプやスワップファイルを読んでパスワードを取得できる可能性です。しかし、もし誰かがそれを行うことができれば、あなたのシステムセキュリティはすでに妥協されていなければなりません...そういうものは、おそらくrootアクセス(または同等のもの)が必要です。また、彼らがrootアクセス権を持っている場合、彼らはあなたのプログラムを "デバッグ"し、のパスワードをキャッチしてから、アプリケーションがそれらを実行することができます。

+0

おそらくコードは残酷すぎる/単純です。配列のセクションだけがワイプされた場合はどうなりますか?そして、カウントが0に設定されているだけです(パスワードの長さのトレースを削除するには)? – Bohemian

+0

大きな懸念事項であるセキュリティは、この懸念の影響を受けません。リセット後に文字列が破損しているのを観察することができます。 –

+0

OK - 実際に使用されている配列のセクションのみをスクラブするようにコードを編集しました。 – Bohemian

1

私がStringに対して行った議論の1つは、誤ってコピーを作成するのが簡単すぎるということです。理論的には文字列を安全に使用することは可能ですが、ライブラリのエコシステム全体は文字列をコピーすることが完全にOKであるという前提に基づいています。結局、すべての制限を考慮すると、文字列は一般的にはこのユースケースにとって便利ではないかもしれません。

+0

しかし 'char []'もコピーできます。あなたのポイントは有効ですが、タイプにかかわらず、パスワードが暗号化されていないことを保証するために、パスワードの周りのコーディング規律を厳密に見直さなければなりません。 – Bohemian

+1

便宜を失うことがあるかもしれません。 –