1. Обзор
В этой статье мы объясним, почему мы должны использовать char[] массив для представления паролей вместо String в Java.
Обратите внимание, что в этом руководстве основное внимание уделяется способам манипулирования паролями в памяти, а не фактическим способам их хранения, которые обычно обрабатываются на уровне сохранения.
Мы также предполагаем, что не можем контролировать формат пароля (например, пароль поступает из стороннего API в виде Строка ). Хотя, казалось бы, очевидно использовать объект типа ). Хотя, казалось бы, очевидно использовать объект типа для манипулирования паролями рекомендуется использовать сами команды Java обуглить[] вместо.
Например, если мы посмотрим на JPasswordField из javax.swing , мы увидим, что метод getText () , который возвращает String , устарел с Java 2 и заменен методом getPassword () , который возвращает char[] .
Итак, давайте рассмотрим несколько веских причин, почему это так.
2. Строки Неизменяемы
Строки s в Java неизменяемы, что означает, что мы не можем изменить их с помощью каких-либо высокоуровневых API. Любое изменение объекта String приведет к созданию новой 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 -> [[email protected]
Мы видим, что в первом случае печатается сам контент, в то время как во втором случае данные не так полезны, что делает char[] менее уязвимым.
4. Заключение
В этой краткой статье мы подчеркнули несколько причин, по которым мы не должны использовать String s для сбора паролей, и почему вместо этого мы должны использовать массивы char [] .
Как всегда, фрагменты кода можно найти на GitHub .