38

Почему предпочтительнее использовать char[] вместо String для хранения паролей?

14

Проблема с использованием String для работы с паролями в Swing

В Swing поле ввода пароля имеет метод getPassword(), который возвращает массив символов (char[]), вместо привычного метода getText(), возвращающего строку (String). Я также наткнулся на рекомендации не использовать String для работы с паролями.

Почему использование String представляет угрозу безопасности при работе с паролями? Это кажется неудобным, поскольку вместо привычного подхода с String приходится использовать char[].

5 ответ(ов)

13

Хотя другие предложения здесь кажутся разумными, есть еще одна важная причина. Используя обычный 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

Как видно, вывод при использовании String показывает сам пароль, тогда как при использовании char[] мы видим только представление массива, что снижает риск случайной утечки конфиденциальной информации.

3

Вопрос: Почему массивы символов (char[]) можно очищать после использования, устанавливая каждый символ в ноль, а строки (String) — нет? Как это влияет на безопасность паролей?

Ответ: Действительно, массивы символов (char[]) предоставляют большую гибкость для работы с конфиденциальными данными, такими как пароли. Когда вы используете char[], вы можете легко очистить массив после использования, установив каждый элемент в ноль. Это позволяет избежать ситуации, когда в памяти остаются следы пароля в виде обычного текста. Например:

char[] password = {'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
// использование пароля...
Arrays.fill(password, '0'); // очищаем массив

С другой стороны, строки в Java являются неизменяемыми (immutable). Это означает, что после создания объекта String вы не можете изменить его содержимое. Даже если вы попытаетесь очистить строку или присвоить ей новое значение, оригинальные данные могут по-прежнему находиться в памяти до тех пор, пока объект не произойдет сборка мусора. Например:

String password = "password";
// использование пароля...
password = null; // старое значение остается в памяти, пока не будет удалено сборщиком мусора.

Таким образом, если злоумышленник сможет получить доступ к памяти, когда используется String, он может увидеть пароль в открытом виде. Это делает использование char[] более безопасным подходом для хранения и обработки конфиденциальной информации.

0

Я не думаю, что это валидное предложение, но, по крайней мере, я могу предположить причину.

Мне кажется, мотивация заключается в том, чтобы убедиться, что вы можете быстро и с уверенностью стереть все следы пароля из памяти после его использования. С помощью char[] вы можете гарантированно перезаписать каждый элемент массива, например, пустым значением. С строкой (String) вы так не сможете поступить.

Но это само по себе не является хорошим ответом; почему бы просто не убедиться, что ссылка на char[] или String не "вырвется" наружу? В таком случае не будет проблемы безопасности. Однако дело в том, что объекты String могут быть теоретически "интернированы" и оставаться в пуле констант. Я предполагаю, что использование char[] исключает эту возможность.

0

Как указывает Джон Скет, единственный способ сделать это — использовать рефлексию.

Если рефлексия подходит для вас, вы можете сделать вот так:

public static void main(String[] args) {
    System.out.println("Пожалуйста, введите пароль");
    // На самом деле не делайте это, это только пример.
    Scanner in = new Scanner(System.in);
    String password = in.nextLine();
    usePassword(password);

    clearString(password);

    System.out.println("пароль: '" + 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);
    }
}

При запуске программа выведет:

Пожалуйста, введите пароль
hello world
пароль: '***********'

Важно: если массив char строки был скопирован в процессе сборки мусора, существует вероятность, что предыдущая копия осталась в памяти.

Эта старая копия не появится в дампе кучи, но если у вас есть прямой доступ к сырой памяти процесса, вы можете ее увидеть. В общем, стоит избегать предоставления кому-либо такого доступа.

0

Строки в Java являются неизменяемыми, и их нельзя изменить после создания. Если создать пароль в виде строки, то могут остаться «голые» ссылки на этот пароль в куче или в пуле строк. Если кто-то сделает дамп кучи процесса Java и внимательно его просканирует, он может догадаться о паролях. Конечно, такие неиспользуемые строки будут подвержены сборке мусора, но это зависит от того, когда сработает сборщик мусора (GC).

С другой стороны, массивы символов (char[]) изменяемы. Как только аутентификация завершена, вы можете перезаписать массив любыми символами, например, заменить все символы на 'M' или на обратные слеши. Таким образом, даже если кто-то сделает дамп кучи, ему может не удастся получить пароли, которые в данный момент не используются. Это дает вам больше контроля — вы можете вручную очистить содержимое объекта, вместо того чтобы ждать, когда сработает сборщик мусора.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь