web-dev-qa-db-ja.com

パスワードのためになぜchar []がStringよりも好まれるのですか?

Swingでは、パスワードフィールドは通常のgetPassword()(returns String)メソッドの代わりにgetText()(returns char[])メソッドを持ちます。同様に、パスワードを扱うのにStringを使わないという提案に出会いました。

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

3159
Ahamed

文字列は不変です。つまり、Stringを作成した後、別のプロセスがメモリをダンプできる場合、( reflection を除いて) ガベージコレクションの前にデータを削除することはできません。 キックイン。

配列を使用すると、処理が完了した後にデータを明示的に消去できます。配列は好きなもので上書きできます。パスワードは、ガベージコレクションの前でもシステムのどこにも存在しません。

確かに、これはisセキュリティ上の問題です-しかし、char[]を使用しても攻撃者にとっての機会の窓が減るだけで、この特定のタイプの攻撃のためだけです。

コメントに記載されているように、ガベージコレクターによって移動される配列は、データのコピーをメモリに残す可能性があります。これは実装固有のものだと思います-ガベージコレクタmayこのようなことを避けるために、すべてのメモリを消去します。たとえそれがあったとしても、char[]には攻撃ウィンドウとして実際の文字が含まれる時間があります。

4100
Jon Skeet

ここに他の提案が有効であるように思われる間、もう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: [C@5829428e
1136
Konrad Garus

公式文書を引用すると、 Java Cryptography Architectureガイド はこれをchar[]Stringパスワードについて述べています(パスワードベースの暗号化についてですが、もちろんこれはより一般的にパスワードについてです)。

パスワードを収集してタイプJava.lang.Stringのオブジェクトに格納するのは論理的に思えます。ただし、注意点は次のとおりです。型ObjectStringsは不変です。つまり、を使用してString [.____の内容を変更(上書き)または消去できるようにするメソッドは定義されていません。 ]使用後この機能はStringオブジェクトをユーザパスワードのような機密性の高い情報を保存するのには不適切なものにします。あなたは常にセキュリティ上重要な情報を集めて char配列に格納するべきです。

Javaプログラミング言語バージョン4.0のセキュアコーディングガイドラインのガイドライン2-2 にも似たようなことが記載されています(もともとはロギングのコンテキストにありますが)。

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

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

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

639
Bruno

文字配列(char[])は、使用後に各文字をゼロに設定し、文字列をゼロに設定しないとクリアできます。誰かが何らかの形でメモリイメージを見ることができる場合、文字列が使用されていればプレーンテキストでパスワードを見ることができますが、char[]が使用されている場合、0でデータをパージした後、パスワードは安全です。

326
alephx

パスワードが不要になったら、パスワードの保存に使用したメモリを上書きする必要があると考える人もいます。これは攻撃者があなたのシステムからパスワードを読まなければならない時間窓を減らし、攻撃者がこれをするためにJVMメモリをハイジャックするためにすでに十分なアクセスを必要とするという事実を完全に無視します。それほど多くのアクセス権を持っている攻撃者があなたの重要な出来事を捕らえることができてこれを完全に役に立たなくすることができます(私の間違いであれば私を修正してください)。

更新

私の答えを更新しなければならないコメントのおかげで。どうやらこれがパスワードがハードドライブに着くことができる時間を減らすのでこれが(非常に)小さなセキュリティ改善を加えることができる2つのケースがある。それでも、ほとんどのユースケースではやり過ぎだと思います。

  • ターゲットシステムが正しく設定されていないか、コアシステムダンプについて妄想している必要があります(システムが管理者によって管理されていない場合でも有効です)。 
  • 攻撃者がハードウェアにアクセスしてデータの漏洩を防止するには、 TrueCrypt (廃止)、 VeraCrypt 、または CipherShed などの方法で、ソフトウェアを過度に偏執的にする必要があります。

可能であれば、コアダンプとスワップファイルを無効にすることで両方の問題を解決できます。しかし、それらは管理者権限を必要とし、機能性を低下させ(使用するメモリが少なくなり)、実行中のシステムからRAMを引き出すことは依然として有効な懸念事項です。

201
josefx
  1. 文字列は不変です パスワードをプレーンテキストとして保存すると、パスワードはガベージコレクタによってクリアされるまでメモリ内で使用可能になります。セキュリティ上の脅威となる、長期間にわたるメモリ。メモリダンプにアクセスできる人なら誰でも平文でパスワードを見つけることができるので 
  2. Javaの推奨 char []を返すJPasswordFieldのgetPassword()メソッドと、セキュリティ上の理由からパスワードをクリアテキストで返すgetText()メソッドを使用する。 
  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 []を使うだけでは十分ではありませんが、より安全にするためにコンテンツを消去する必要があります。プレーンテキストではなくハッシュ化または暗号化されたパスワードで作業し、認証が完了したらすぐにメモリから消去することもお勧めします。

131

これは有効な提案ではないと思いますが、少なくともその理由は推測できます。

動機は、使用後すぐに確実にメモリ内のパスワードの痕跡をすべて消去できるようにすることです。 char[]を使用すると、配列の各要素を空白または何か確実に上書きすることができます。 Stringの内部値をそのように編集することはできません。

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

76
Sean Owen

答えはすでに与えられていますが、私が最近発見した問題をJava標準ライブラリと共有したいと思います。パスワード文字列をいたるところでchar[]に置き換えることに今は細心の注意を払っていますが(もちろんこれは良いことです)、セキュリティ上重要な他のデータは、メモリから消去する際に見逃されているようです。

私は考えています。 PrivateKey クラス。 PKCS#12ファイルから秘密RSAキーをロードし、それを使用して何らかの操作を実行するというシナリオを考えます。この場合、パスワードだけを盗聴しても、キーファイルへの物理的なアクセスが適切に制限されている限りは役に立ちません。攻撃者としては、パスワードの代わりに直接キーを入手した方がはるかに良いでしょう。目的の情報がリークされている可能性がある、コアダンプ、デバッガセッション、またはスワップファイルはほんの一例です。

そして結局のところ、対応する情報を形成するバイトを消去することを可能にするAPIがないので、PrivateKeyの個人情報をメモリから消去することはできません。

This paper はこの状況が潜在的に悪用される可能性があることを説明しているので、これは悪い状況です。

たとえばOpenSSLライブラリは、秘密鍵が解放される前に重要なメモリセクションを上書きします。 Javaはガベージコレクションされているため、キーを使用した直後に適用されるJavaキーのプライベート情報を消去して無効にする明示的なメソッドが必要になります。 

61
emboss

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: '***********'

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

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

46
Peter Lawrey

編集:1年のセキュリティ研究の後、この答えに戻って、実際にプレーンテキストのパスワードを比較するというかなり不幸な意味合いを持っていることに気付きました。しないでください。 ソルトと適切な反復回数で安全な一方向ハッシュを使用 。ライブラリを使用することを検討してください。このようなものを正しく取得することは困難です。

元の答え: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;
}

タイミング攻撃に関するその他のリソース:

39
Graph Theory

これらがすべての理由です。パスワードには String の代わりに char [] を選ぶべきです。

1. Javaでは文字列は不変なので、パスワードをプレーンテキストとして格納すると、ガベージコレクタがそれをクリアするまでメモリ内で使用可能になります。また、Stringは再利用のためにStringプールで使用されるため長期間メモリ内に残る可能性が非常に高いため、セキュリティ上の脅威となります。 

メモリダンプにアクセスできる人はだれでも平文でパスワードを見つけることができるので、平文ではなく暗号化されたパスワードを常に使用すべきもう1つの理由です。文字列は不変であるため、変更すると新しい文字列が生成されるため、文字列の内容を変更することはできませんが、char []を使用しても、すべての要素を空白またはゼロに設定できます。そのため、パスワードを文字配列に格納すると、パスワードを盗むことによるセキュリティ上のリスクが明らかに軽減されます。

2 セキュリティ上の理由からパスワードを平文で返す廃止予定のgetText()メソッドではなく、char []を返すJPasswordFieldのgetPassword()メソッドの使用をJava自体が推奨しています。 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: [C@110b053

このブログからの参照 。これがお役に立てば幸いです。

37
Human Being

使用後に手動でそれをクリーンアップしない限り、char配列があなたに対文字列を与えることは何もありません、そして私は誰も実際にそれをしているのを見たことがありません。だから私にとってchar []対Stringの好みは少し誇張されています。

を見てください 広く使われています Spring Securityのライブラリ here そして自分自身に尋ねる - Spring Securityの人たちは無能か、char []のパスワードはあまり意味がない。厄介なハッカーがあなたのRAMのメモリダンプをつかむとき、たとえあなたがそれらを隠すために洗練された方法を使ったとしても、彼女はすべてのパスワードを手に入れるでしょう。

ただし、Javaは常に変化しており、 Java 8のString Deduplication feature のような怖い機能は、知らないうちにStringオブジェクトを埋め込むことがあります。しかしそれは違う会話です。

33
Oleg Mikheev

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

反対に、認証が完了するとすぐにchar []は変更可能になります。それらはすべてのMやバックスラッシュのような任意の文字で上書きすることができます。今、誰かがヒープダンプを取ったとしても、彼は現在使用されていないパスワードを取得することができないかもしれません。これにより、オブジェクトの内容を自分でクリアするのに対して、GCがそれを実行するのを待つのと同じ意味で、より細かく制御できます。

29
Geek

1)パスワードをプレーンテキストとして保存するとJavaでは文字列が不変になるため、ガベージコレクタがパスワードをクリアするまでメモリ内で使用できるようになります。長期間にわたってセキュリティが脅かされます。メモリダンプへのアクセス権を持っている人なら誰でも平文でパスワードを見つけることができるので、それは別の理由です。平文よりも常に暗号化されたパスワードを使うべきです。文字列は不変なので、変更すると新しい文字列が生成されるため、文字列の内容を変更することはできません。一方、char []の場合は、すべての要素を空白またはゼロに設定できます。そのため、パスワードを文字配列に格納すると、パスワードを盗むことによるセキュリティ上のリスクが軽減されます。

2)Java自体が、char []を返すJPasswordFieldのgetPassword()メソッドと、セキュリティ上の理由からパスワードをクリアテキストで返すgetText()メソッドの使用を推奨します。 Javaチームからのアドバイスに従い、それに反対するのではなく標準に従うことをお勧めします。

16
Neeraj Gahlawat

短く直接的な答えは、char[]は可変であるがStringオブジェクトはそうではないためです。

JavaのStringsは不変オブジェクトです。そのため、一度作成したファイルを修正できないのは、その内容をメモリから削除する唯一の方法は、それらをガベージコレクションすることです。それは、オブジェクトによって解放されたメモリが上書きされ、データが消えたときに初めて起こります。

現在、Javaでのガベージコレクションは保証された間隔では発生しません。そのためStringは長期間メモリ内に残ることがあり、この間にプロセスがクラッシュした場合、文字列の内容はメモリダンプまたはログに記録されることがあります。 

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

16
Pritam Banerjee

文字列は不変であり、それは文字列プールに行きます。一度書き込まれると、上書きすることはできません。

char[]は、パスワードを使用した後に上書きする必要がある配列です。これがその方法です。

char[] passw = request.getPassword().toCharArray()
if (comparePasswords(dbPassword, passw) {
 allowUser = true;
 cleanPassword(passw);
 cleanPassword(dbPassword);
 passw=null;
}

private static void cleanPassword (char[] pass) {
 for (char ch: pass) {
  ch = '0';
 }
}

攻撃者がそれを使用することができるシナリオの1つはクラッシュダンプです - JVMがクラッシュしてメモリダンプを生成する - あなたはパスワードを見ることができるでしょう。

それは必ずしも悪意のある外部からの攻撃者ではありません。これは、監視目的でサーバーにアクセスできるサポートユーザーになる可能性があります。彼はクラッシュダンプを覗いてパスワードを見つけることができました。

15
ACV

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

文字配列では、パスワードの目的が達成されたら、配列の内容を変更または消去できます。配列の元の内容は、変更された後やガベージコレクションが開始される前であってもメモリ内に見つかりません。

セキュリティ上の理由から、パスワードを文字配列として格納することをお勧めします。

11
Saathvik

StringまたはChar []へのパスワードの使用は常に議論の余地があります。どちらの方法にも独自の有用性と欠点があるためです。それはどのユーザーが満たされることを期待している必要性によります。以下の行は、どのコンテナを使用するかを理解するのに役立つかもしれません。 JavaのStringは不変であるため、文字列を操作しようとするたびに新しいObjectを作成し、既存のものはそのままです。これは、パスワードをStringとして格納することの利点として考えられますが、その一方でです。文字列オブジェクトは使用後もメモリに残ります。それで、どういうわけか誰かがメモリの場所を手に入れた場合、その場所に保存されているあなたのパスワードを簡単に追跡することができます。変更可能なChar []に来る間そのため、使用しなくなった後はクリーンアップされ、保存した情報についてだれも知ることができなくなります。

上記の状況に基づいて、StringまたはChar []のどちらを要件として使用するのかというアイデアを得ることができます。

ありがとう。

1
Neeraj

一言で言えば、これはあなたがそれを使い終わった後に保存されたパスワードがどうなるかのためです。 

文字列は、使用を停止した後も(潜在的には)メモリ内に残る可能性がありますが、文字配列は空にすることができるため、データは含まれなくなります。

これはすべて、 ガベージコレクタ および 不変オブジェクト の動作方法と関係があります。

0