Почему для хранения паролей в Java предпочитают char[], а не String?

«Почему для хранения паролей в Java предпочитают char[], а не String?» — вопрос из категории Безопасность, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Использование char[] вместо String для хранения конфиденциальных данных (паролей, PIN-кодов) — это рекомендация по безопасности, основанная на особенностях управления памятью в JVM.

Основные причины:

  1. Немедленная очистка памяти.

    • Массив char[] можно явно перезаписать после использования, удалив конфиденциальные данные из памяти.
    • Объект String неизменяем (immutable). Присвоение новой строки или null не стирает старые данные в памяти — они останутся там до сборки мусора.
  2. Риск утечки в дамп памяти.

    • String часто интернируются и хранятся в пуле строк (String Pool), который может быть выгружен в дамп памяти (heap dump) для отладки, что подвергает пароли риску.
    • char[], как правило, не подвергается такой оптимизации.
  3. Логирование и сериализация.

    • String может быть неявно залогирован или сериализован (например, в thread dump), что приведет к утечке. С char[] такого не происходит.

Практический пример:

import java.util.Arrays;

public class PasswordStorage {
    public boolean authenticate(char[] inputPassword) {
        char[] correctPassword = getPasswordFromSecureStorage(); // 's','e','c','r','e','t'

        // Сравнение с постоянным временем (защита от timing-атак)
        boolean result = Arrays.equals(inputPassword, correctPassword);

        // ВАЖНО: Немедленная очистка конфиденциальных данных из памяти
        Arrays.fill(correctPassword, '');
        Arrays.fill(inputPassword, '');

        return result;
    }
}

Рекомендации:

  • Используйте char[] для временного хранения пароля в памяти.
  • Немедленно перезаписывайте массив нулями () после использования.
  • Для долгосрочного хранения используйте хеши (с солью) или специализированные хранилища (например, Secrets в Spring Cloud).
  • Рассмотрите использование javax.security.auth.Destroyable интерфейса для объектов, содержащих конфиденциальные данные.