← Назад к вопросам

Как хранить пароль?

2.0 Middle🔥 191 комментариев
#Soft Skills и рабочие процессы

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как хранить пароль в веб-приложениях: принципы и практика

Ключевой принцип безопасного хранения паролей — никогда не хранить пароли в открытом (plaintext) виде. Пароль пользователя должен быть преобразован в необратимую форму, которую невозможно декодировать обратно в исходный пароль. Этот процесс называется хэшированием (hashing).

Основные этапы и технологии

1. Выбор алгоритма хэширования

Современные стандарты требуют использования специальных алгоритмов, разработанных именно для паролей, которые являются медленными и устойчивыми к brute-force атакам.

  • Устаревшие и небезопасные алгоритмы: MD5, SHA-1, SHA-256 (в чистом виде). Они слишком быстрыы для вычисления, что позволяет атакующим быстро проверять миллионы возможных паролей.
  • Современные и рекомендуемые алгоритмы:
    • bcrypt: Самый распространенный выбор. Внутренне использует алгоритм Blowfish, позволяет настраивать «стоимость» (cost factor) — количество итераций, что замедляет вычисление и адаптируется к росту мощности hardware.
    • scrypt: Более продвинутый алгоритм, который не только требует много вычислений, но и много памяти, что делает атаки на специализированном hardware (например, GPU) еще более сложными.
    • Argon2: победитель конкурса Password Hashing Competition в 2015 году. Считается современным state-of-the-art алгоритом. Имеет конфигурируемые параметры для сопротивления различным типам атак (по времени, памяти, параллельным вычислениям).

2. Добавление «соли» (Salt)

«Соль» (salt) — это случайная строка, уникальная для каждого пароля, которая добавляется к паролю перед хэшированием.

// Пример генерации соли и хэширования с использованием bcrypt (Node.js)
const bcrypt = require('bcrypt');
const saltRounds = 12; // "стоимость" - число итераций

async function hashPassword(plainPassword) {
    // Генерируем уникальную соль
    const salt = await bcrypt.genSalt(saltRounds);
    // Хэшируем пароль вместе с солью
    const hash = await bcrypt.hash(plainPassword, salt);
    // В результате hash уже содержит соль внутри себя
    return hash;
}

Цель использования соли:

  • Предотвращает использование rainbow tables (предварительно вычисленных таблиц хэшей для популярных паролей). Даже если два пользователя имеют одинаковый пароль, их хэши будут разными из-за разной соли.
  • Защищает от атак на конкретного пользователя, так как атакующий должен вычислять хэши для каждого пароля отдельно с его уникальной солью.

3. Сравнение паролей (верификация)

При аутентификации пользователя мы не «дешифруем» хэш. Мы берем предоставленный пароль, комбинируем его с солью, хранящейся в этом же хэше, и выполняем тот же алгоритм хэширования. Затем сравниваем результат с хранимым хэшем.

async function verifyPassword(plainPassword, storedHash) {
    // bcrypt.compare самостоятельно извлекает соль из storedHash
    // и сравнивает результат хэширования plainPassword с этой солью
    // с storedHash.
    const isMatch = await bcrypt.compare(plainPassword, storedHash);
    return isMatch;
}

Практические шаги для фронтенд-разработчика

Фронтенд разработчик НИКОГДА не должен заниматься непосредственно хэшированием паролей клиентской стороны. Это задача backend сервера. Однако фронтенд играет критическую роль в безопасной передаче пароля на backend.

  1. Всегда используйте HTTPS/TLS. Передача пароля от клиента к серверу должна происходить только по защищенному соединению.
  2. Не обрабатывайте пароль лишний раз на клиенте. Например, избегайте «хэширования пароля на клиенте перед отправкой». Это создает ложное ощущение безопасности и может даже ослабить ее (например, если клиентский хэш становится фактическим паролем для системы).
  3. Передавайте пароль в теле запроса (например, POST), а не в URL или заголовках, которые могут легче попасть в лог-файлы.
  4. Реализуйте базовые проверки на клиенте для удобства пользователя (например, минимальная длина), но помните, что все окончательные проверки и хэширование происходят на сервере.
// Пример отправки пароля на backend (фронтенд)
async function loginUser(username, password) {
    // Никакого хэширования здесь! Просто отправляем.
    const response = await fetch('/api/login', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            username: username,
            password: password // Пароль в чистом виде, но отправляется по HTTPS
        })
    });
    return response.json();
}

Что хранить в базе данных?

В таблице пользователей вы храните:

  • password_hash — строка, содержащая результат работы алгоритма (например, bcrypt), который уже включает в себя соль и параметры алгоритма.
  • НИКАКИХ других полей, связанных с паролем. Не храните соль отдельно (она внутри хэша), не храните пароль в открытом виде или в «зашифрованном» виде (encrypted), который можно обратно расшифровать.

Дополнительные меры безопасности

  • Учет попыток аутентификации и задержки (rate limiting): Защита от brute-force атак на endpoint логина.
  • Обязательное использование параметров высокой «стоимости» (cost factor) для алгоритма хэширования. Например, для bcrypt рекомендуется значение не менее 12.
  • Регулярный аудит и обновление процедур. Алгоритмы и best practices эволюционируют. Процедуры хэширования должны периодически пересматриваться.

Итог: Для фронтенд разработчика ключевая задача — обеспечить безопасную передачу пароля (HTTPS) и понимать, что вся реальная работа по его защищенному хранению происходит на backend сервере с использованием современных алгоритмов хэширования паролей, таких как bcrypt, scrypt или Argon2.