Перейти к основному содержимому

Использовать массив char[] над строкой для управления паролями в Java?

· 4 мин. чтения

1. Обзор

В этой статье мы объясним, почему мы должны использовать массив char[] для представления паролей вместо String в Java.

Обратите внимание, что в этом руководстве основное внимание уделяется способам манипулирования паролями в памяти, а не фактическим способам их хранения, которые обычно обрабатываются на уровне сохраняемости.

Мы также предполагаем, что не можем контролировать формат пароля (например, пароль исходит от стороннего API в виде строки ). Хотя использование объекта типа java.lang.String для управления паролями кажется очевидным , сами разработчики Java рекомендуют вместо этого использовать char[] .

Например, если мы посмотрим на JPasswordField javax.swing , то увидим, что метод getText() , который возвращает String , устарел, начиная с Java 2, и заменен методом getPassword() , который возвращает char[] .

Итак, давайте рассмотрим несколько веских причин, почему это так.

2. Строки неизменны

Строки в Java являются неизменяемыми, что означает, что мы не можем изменить их с помощью каких-либо высокоуровневых API. Любое изменение объекта 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

Как мы видим, после того, как мы попытались заменить содержимое исходной строки , значение осталось прежним, и метод hashCode() не вернул другое значение при том же выполнении приложения, что означает, что исходная строка осталась нетронутой.

А для массива 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 .