java - 構造 - newstringutf 解放




パスワードの文字列よりもchar[]が優先されるのはなぜですか? (12)

  1. 文字列は、パスワードをプレーンテキストとして保存すると、Javaでは不変です。ガーベジコレクタがそれをクリアし、文字列が再利用性のためにStringプールで使用されるため、メモリ内に長時間留まる可能性はかなり高いですこれはセキュリティの脅威となる。 メモリダンプにアクセスできる人は誰でもクリアテキストでパスワードを見つけることができるので
  2. char []を返すJPasswordFieldのgetPassword()メソッドと、セキュリティ上の理由を明記したクリアテキストでパスワードを返すgetText()メソッドを使用するJavaの推奨
  3. toString()は、ログファイルやコンソールにプレーンテキストを印刷する危険性が常にありますが、Arrayを使用する場合は、メモリの場所が印刷される代わりに配列の内容は印刷されません。

    String strPwd = "passwd";
    char[] charPwd = new char[]{'p','a','s','s','w','d'};
    System.out.println("String password: " + strPwd );
    System.out.println("Character password: " + charPwd );
    

    文字列パスワード:passwd

    文字パスワード:[C @ 110b2345

最終的な考え方: char []を使用するだけでは十分ではありませんが、より安全なコンテンツを消去する必要があります。 私はまた、プレーンテキストの代わりにハッシュまたは暗号化されたパスワードで作業し、認証が完了するとすぐにメモリからクリアすることをお勧めします。

Swingでは、パスワードフィールドには通常のgetText()String返す)メソッドの代わりにgetPassword()char[] )メソッドがあります。 同様に、パスワードを処理するためにStringを使用しないことを提案しました。

なぜStringはパスワードに関してセキュリティに脅威を与えますか? char[]使用は不便です。


Jon Skeetが述べるように、リフレクションを使用する以外は方法はありません。

ただし、リフレクションがオプションの場合は、これを行うことができます。

public static void main(String[] args) {
    System.out.println("please enter a password");
    // don't actually do this, this is an example only.
    Scanner in = new Scanner(System.in);
    String password = in.nextLine();
    usePassword(password);

    clearString(password);

    System.out.println("password: '" + password + "'");
}

private static void usePassword(String password) {

}

private static void clearString(String password) {
    try {
        Field value = String.class.getDeclaredField("value");
        value.setAccessible(true);
        char[] chars = (char[]) value.get(password);
        Arrays.fill(chars, '*');
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}

実行時

please enter a password
hello world
password: '***********'

メモ:文字列のchar []がGCサイクルの一部としてコピーされている場合、前のコピーがメモリのどこかにある可能性があります。

この古いコピーはヒープダンプには表示されませんが、プロセスの生のメモリに直接アクセスできる場合は、それを見ることができます。 一般的に、そのようなアクセス権を持つ人は避けてください。


編集:セキュリティ研究の1年後にこの答えに戻って、私はそれがあなたが実際に平文のパスワードを比較するという不幸な含意を行うことを認識します。 しないでください。 saltと妥当な回数の反復を使用して、安全な一方向ハッシュを使用します 。 ライブラリを使用することを検討してください。これは正しいことです。

元の答え: String.equals()が短絡評価を使用しているため、タイミング攻撃の影響を受けやすいのはどうですか? 可能性は低いかもしれませんが、文字列の正しい順序を決定するために理論的にパスワードの比較を行うことができます。

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        // Quits here if Strings are different lengths.
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            // Quits here at first different character.
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

タイミング攻撃に関するいくつかのリソース:


あなたが使用後に手作業でそれをきれいにしない限り、char配列があなたにvs Stringを与えるものは何もありません。実際にそれをしている人は誰も見ていません。 だから私には、char []とStringの選択は少し誇張されています。

here 広く使われている Spring Securityライブラリを見て、自分自身に質問してください。Spring Securityの人は無能であるか、char []パスワードはあまり意味がありません。 厄介なハッカーがあなたのRAMのメモリダンプをつかんだら、洗練された方法でそれらを隠していても、すべてのパスワードを取得するようにしてください。

しかし、Javaは常に変化し、 Java 8のString Deduplication機能のようないくつかの厄介な機能は 、あなたが知らないうちにStringオブジェクトをインターンにするかもしれません。 しかし、それは別の会話です。


これらはすべての理由であり、パスワードの代わりに文字列の代わりにchar []配列を選択する必要があります。

1.文字列はJavaで不変です。パスワードをプレーンテキストとして保存すると、ガーベッジコレクタがそれをクリアし、Stringが再利用性のためにStringプールで使用されるため、メモリ内に長時間留まる可能性がかなり高くなりますセキュリティ上の脅威となる可能性があります。 メモリダンプにアクセスできるユーザーは誰でもクリアテキストでパスワードを見つけることができるので、プレーンテキストよりも暗号化されたパスワードを常に使用する必要があります。 文字列は不変なので、文字列の内容を変更する方法はありません。変更すると新しい文字列が生成されますが、char []の場合はすべての要素を空白またはゼロに設定できます。 したがって、パスワードを文字配列に格納することは、パスワードを盗むセキュリティリスクを明らかに緩和します。

2. Java自体は、セキュリティ理由を示すクリアテキストでパスワードを返すchar []と廃止されたgetText()メソッドを返すJPasswordFieldのgetPassword()メソッドを使用することを推奨します。 Javaチームからのアドバイスに従い、標準に準拠するのは良いことです。

3.文字列を使用すると、ログファイルやコンソールにプレーンテキストを印刷する危険性が常にありますが、Arrayを使用すると、メモリの場所が印刷される代わりに配列の内容が印刷されません。 本当の理由ではありませんが、まだ理にかなっています。

    String strPassword="Unknown";
    char[] charPassword= new char[]{'U','n','k','w','o','n'};
    System.out.println("String password: " + strPassword);
    System.out.println("Character password: " + charPassword);

    String password: Unknown
    Character password: [[email protected]

参照先: http://javarevisited.blogspot.com/2012/03/why-character-array-is-better-than.html : http://javarevisited.blogspot.com/2012/03/why-character-array-is-better-than.htmlこれが役立つことを願っています。


他の提案は有効であるように見えますが、他にも1つの理由があります。 プレーンStringを使用すると、 誤ってパスワードをログ 、モニター、またはその他の安全でない場所に印刷する可能性が非常に高くなりますchar[]はあまり脆弱ではありません。

このことを考慮:

public static void main(String[] args) {
    Object pw = "Password";
    System.out.println("String: " + pw);

    pw = "Password".toCharArray();
    System.out.println("Array: " + pw);
}

印刷物:

String: Password
Array: [[email protected]

正式な文書を引用するために、 Java暗号アーキテクチャーのガイドでは、 char[]Stringパスワードについて説明しています(パスワードベースの暗号化についてですが、これはもっと一般的なパスワードです)。

java.lang.String型のオブジェクトにパスワードを収集して格納するのは理にかなっているようです。 ただし、ここでは注意してください: String型のObjectは不変です。つまり、使用後にStringの内容を変更(上書き)またはゼロ化するメソッドが定義されていません。 この機能により、 Stringオブジェクトは、ユーザーのパスワードなどの機密情報を格納するのに適していません。 代わりに、常に機密情報を収集してchar配列に格納する必要があります。

Javaプログラミング言語、バージョン4.0のセキュアコーディングガイドラインのガイドライン2-2も同様です(もともとはログのコンテキストにあります)。

ガイドライン2-2:機密性の高い情報を記録しない

社会保障番号(SSN)やパスワードなど、一部の情報は非常に機密です。 この情報は、必要以上に長く保管したり、管理者が見た場合には保管しないでください。 たとえば、ログファイルには送信しないでください。その存在を検索で検出できないようにする必要があります。 一部の一時的なデータは、char配列などの変更可能なデータ構造に保持され、使用直後にクリアされます。 データ構造を消去すると、オブジェクトがメモリ内でプログラマに透過的に移動されるので、典型的なJavaランタイムシステムでの有効性が低下しました。

このガイドラインは、扱っているデータの意味論的知識を持たない下位レベルのライブラリの実装と使用にも影響します。 例として、低レベルの文字列解析ライブラリは、動作するテキストをログに記録することがあります。 アプリケーションは、ライブラリとSSNを解析することができます。 これにより、ログファイルへのアクセス権を持つ管理者がSSNを利用できるようになります。


私はこれが有効な提案だとは思わないが、少なくとも理由を推測できる。

私はその動機づけが、パスワードを使用した後、速やかにかつ確実にすべてのパスワードのトレースを消去できることを確認したいと思っています。 char[]使うと配列の各要素を空白などで上書きすることができます。 そのようにString内部値を編集することはできません。

しかしそれだけでは良い答えではありません。 char[]Stringへの参照がエスケープしないようにしてください。 セキュリティ上の問題はありません。 しかし、 Stringオブジェクトは理論的にはintern()でき、定数プールの中に生きています。 私はchar[]を使ってこの可能性を禁止していると思います。


1)パスワードをプレーンテキストとして保存すると、文字列はJavaでは不変なので、ガベージコレクタがそれをクリアし、Stringが再利用可能にStringプールで使用されるため、メモリ内に残る可能性が非常に高い長い間、セキュリティ上の脅威となっています。メモリダンプにアクセスできる人は誰でもクリアテキストでパスワードを見つけることができるので、プレーンテキストよりも常に暗号化されたパスワードを使用する必要があります。文字列は不変なので、文字列の内容を変更することはできません。変更すると新しい文字列が生成されますが、char []の場合はすべての要素を空白またはゼロに設定できます。したがって、パスワードを文字配列に格納すると、パスワードを盗むセキュリティリスクが軽減されます。

2)JPasswordFieldのgetPassword()メソッドを使用することをお勧めします。これは、char []とgetText()メソッドを返すJPasswordFieldのメソッドです。Javaチームからのアドバイスに従い、標準に準拠するのではなく、標準に準拠することは良いことです。


javaの文字列は不変です。したがって、文字列が作成されるたびに、それはガベージコレクションされるまでメモリに残ります。したがって、メモリにアクセスできる人なら誰でも文字列の値を読み取ることができます。
文字列の値が変更されると、新しい文字列が作成されます。そのため、元の値と変更された値の両方がガベージコレクションされるまでメモリに残ります。

文字配列では、パスワードの目的が達成されると、配列の内容を変更または消去することができます。配列の元の内容は、ガベージコレクションが開始される前でさえ、変更された後でもメモリ内に検出されません。

セキュリティ上の問題があるため、パスワードを文字配列として格納する方がよいでしょう。


文字列は不変であり、作成後は変更できません。パスワードを文字列として作成すると、ヒープ上または文字列プール上のパスワードへの迷いの参照が残されます。誰かがJavaプロセスのヒープ・ダンプを取って慎重にスキャンすると、彼はパスワードを推測できるかもしれません。もちろん、これらの使用されていない文字列はガベージコレクションされますが、GCがいつ起動するかによって異なります。

反対側では、char []は認証が完了するとすぐに変更可能です。すべてのMやバックスラッシュのような文字で上書きすることができます。現在、誰かがヒープダンプを取ったとしても、現在使用されていないパスワードを取得できない可能性があります。これにより、GCがそれを行うのを待っている間にオブジェクトの内容をクリアするような感覚で、より多くの制御が可能になります。


短くて直接的な答えはchar[]Stringオブジェクトが存在しない間に変更可能であるためです。

StringsJavaでは、不変のオブジェクトです。そのため、一度作成した変更はできません。そのため、コンテンツをメモリから削除する唯一の方法は、ガベージコレクションを行うことです。オブジェクトによって解放されたメモリを上書きすることができ、データがなくなるときだけになります。

Javaでのガベージコレクションは、保証された間隔では行われません。このStringようにして、長い間メモリ内に存続する可能性があります。この間にプロセスがクラッシュすると、文字列の内容がメモリダンプやログに記録される可能性があります。

文字配列を使用すると、パスワードを読み取って、できるだけ早く作業を完了させ、すぐに内容を変更することができます。







char