1. 概要

この記事では、JavaのStringの代わりにchar[]配列を使用してパスワードを表す必要がある理由を説明します。

このチュートリアルは、通常は永続層で処理される実際のパスワードの保存方法ではなく、メモリ内のパスワードの操作方法に焦点を当てていることに注意してください。

また、パスワードの形式を制御することはできないと想定しています(例: パスワードは、Stringの形式でサードパーティのAPIから取得されます。 パスワードの操作にjava.lang.String タイプのオブジェクトを使用することは明らかですが、Javaチーム自身が代わりに char[]を使用することをお勧めします。 

たとえば、javax.swingJPasswordFieldを見ると、 String[を返すメソッドgetText()がわかります。 X159X]はJava2以降非推奨になり、 char []を返すgetPassword()メソッドに置き換えられました。

それでは、それが当てはまるいくつかの強力な理由を探りましょう。

2. 文字列は不変です

JavaのStringは不変です。つまり、高レベルのAPIを使用して変更することはできません。 String オブジェクトを変更すると、新しい String が生成され、古いオブジェクトがメモリに保持されます。

したがって、 String に保存されているパスワードは、ガベージコレクターがクリアするまでメモリで使用できます。 いつ発生するかを制御することはできませんが、 Strings は再利用のために文字列プールに保持されるため、この期間は通常のオブジェクトよりも大幅に長くなる可能性があります。

したがって、メモリダンプにアクセスできる人は誰でも、メモリからパスワードを取得できます。

Stringの代わりにchar[] 配列を使用すると、目的の作業が終了した後、データを明示的にワイプできます。 このようにして、ガベージコレクションが行われる前でも、パスワードがメモリから確実に削除されます。

ここで、今説明した内容を示すコードスニペットを見てみましょう。

String の最初:

System.out.print("Original String password value: ");
System.out.println(stringPassword);
System.out.println("Original String password hashCode: "
  + Integer.toHexString(stringPassword.hashCode()));

String newString = "********";
stringPassword.replace(stringPassword, newString);

System.out.print("String password value after trying to replace it: ");
System.out.println(stringPassword);
System.out.println(
  "hashCode after trying to replace the original String: "
  + Integer.toHexString(stringPassword.hashCode()));

出力は次のようになります。

Original String password value: password
Original String password hashCode: 4889ba9b
String value after trying to replace it: password
hashCode after trying to replace the original String: 4889ba9b

char [] の場合:

char[] charPassword = new char[]{'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};

System.out.print("Original char password value: ");
System.out.println(charPassword);
System.out.println(
  "Original char password hashCode: " 
  + Integer.toHexString(charPassword.hashCode()));

Arrays.fill(charPassword, '*');

System.out.print("Changed char password value: ");
System.out.println(charPassword);
System.out.println(
  "Changed char password hashCode: " 
  + Integer.toHexString(charPassword.hashCode()));

出力は次のとおりです。

Original char password value: password
Original char password hashCode: 7cc355be
Changed char password value: ********
Changed char password hashCode: 7cc355be

ご覧のとおり、元の String のコンテンツを置き換えようとした後、値は同じままであり、 hashCode()メソッドは同じ実行で異なる値を返しませんでしたアプリケーション。つまり、元のStringはそのまま残りました。

また、 char [] 配列の場合、同じオブジェクトのデータを変更することができました。

3. 誤ってパスワードを印刷する可能性があります

char [] アレイでパスワードを操作することの別の利点は、コンソール、モニター、またはその他の多かれ少なかれ安全でない場所にパスワードが誤って記録されるのを防ぐことです。

次のコードをチェックしてみましょう:

String passwordString = "password";
char[] passwordArray = new char[]{'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
System.out.println("Printing String password -> " + passwordString);
System.out.println("Printing char[] password -> " + passwordArray);

出力付き:

Printing String password -> password
Printing char[] password -> [C@6e8cf4c6

前者の場合はコンテンツ自体が印刷されますが、後者の場合はデータがあまり役に立たないため、 char[]の脆弱性が低くなります。

4. 結論

この簡単な記事では、パスワードの収集に String を使用しない理由と、代わりに char[]配列を使用する必要がある理由をいくつか強調しました。

いつものように、コードスニペットはGitHubにあります。