Webrium
Подписаться

Пароли пользователей: как хранить и что проверить сегодня

Как определить уязвимости в хранении паролей и быстро исправить их в своём проекте

Три показателя безопасности паролей: два в норме, один за шкалой — отсутствие солиБез соли

Как хранить пароли пользователей: проверка и исправление за час

Пароли хранят неправильно не из-за незнания правил, а потому что не проверяют текущую реализацию. В этом материале мы разберём конкретные шаги для проверки и исправления хранения паролей в вашем проекте. Никаких абстрактных рекомендаций - только то, что можно проверить в коде или базе данных сегодня.

Почему простое хеширование не защищает пароли

Первое, что приходит в голову при обсуждении безопасности паролей: “Мы не храним пароли в открытом виде, мы храним их хеши”. Это необходимое, но недостаточное условие. Хеширование само по себе не защищает от ключевых угроз безопасности:

Скорость перебора паролей. Быстрые алгоритмы хеширования позволяют злоумышленникам вычислять миллионы хешей в секунду. Чем быстрее работает алгоритм, тем больше паролей можно перебрать за то же время.

Уязвимость к коллизиям. Некоторые алгоритмы допускают ситуации, когда разные пароли дают одинаковый хеш. Это позволяет злоумышленникам использовать один пароль для доступа к нескольким аккаунтам.

Отсутствие индивидуальной соли. Без уникальной соли для каждого пользователя злоумышленники могут использовать предварительно вычисленные таблицы хешей для массового взлома паролей.

Низкая вычислительная сложность. Алгоритмы, не требующие значительных ресурсов, позволяют злоумышленникам перебирать пароли быстрее, чем система успеет обнаружить атаку.

Поэтому первый шаг - не просто проверить наличие хеширования, а определить, какой именно алгоритм используется и как он реализован.

Где искать пароли в системе

В большинстве приложений пароли хранятся в таблице пользователей, обычно называемой users или аналогично. При осмотре этой таблицы обратите внимание на следующие признаки:

Тип данных столбца. Если пароли хранятся как обычные строки без специальных атрибутов, это может указывать на отсутствие хеширования. Хешированные пароли должны занимать фиксированное количество байт, соответствующее используемому алгоритму.

Длина значений. Хеши разных алгоритмов имеют разную длину. Если длина хеша не соответствует ожидаемой для используемого алгоритма, вероятно, используется слабый метод хеширования или пароли хранятся в открытом виде.

Название столбца. Столбец с названием password или pass может указывать на отсутствие хеширования. Хорошая практика - использовать названия вроде password_hash или pwd_hash, чтобы сразу было понятно, что хранится хеш.

Для проверки выполните простой запрос к базе данных:

SELECT password_column FROM users LIMIT 1;

Если результат выглядит как обычный текст (например, qwerty123), пароли хранятся в открытом виде. Если результат - строка из случайных символов с определённой структурой, вероятно, используется хеширование.

Как определить используемый алгоритм хеширования

Если пароли хешируются, следующий шаг - определить конкретный алгоритм. Вот как это сделать для основных алгоритмов:

Bcrypt

Хеши bcrypt легко узнать по характерному формату. Они начинаются с префикса $2a$, $2b$ или $2y$, за которым следует число, обозначающее стоимость вычислений. Длина хеша всегда фиксированная. Пример:

$2a$10$xJwL5v5z3b3Z3b3Z3b3Z3uK5v5z3b3Z3b3Z3b3Z3b3Z3b3Z3b3Z3b3Z3b3Z3

Bcrypt - один из самых надёжных алгоритмов для хеширования паролей. Он специально разработан для этой задачи и требует значительных вычислительных ресурсов, что затрудняет перебор паролей.

SHA-256 и SHA-512

Хеши SHA-256 и SHA-512 выглядят как строки из шестнадцатеричных символов фиксированной длины. SHA-256 всегда имеет длину 64 символа, а SHA-512 - 128 символов. Пример SHA-256:

5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8

Если вы видите такой хеш, проверьте наличие соли. Без соли SHA-256 уязвим для атак с использованием предварительно вычисленных таблиц.

PBKDF2

Хеши PBKDF2 обычно содержат параметры алгоритма, соли и количества итераций. Пример:

pbkdf2_sha256$100000$abc123$saltedhashvalue

Это хороший выбор, но важно убедиться, что количество итераций достаточно велико. Чем больше итераций, тем сложнее перебрать пароли.

Argon2

Хеши Argon2 начинаются с $argon2i$ или $argon2id$ и содержат параметры вычислений. Пример:

$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG

Argon2 - один из самых современных алгоритмов, но он требует больше ресурсов, чем другие варианты.

Устаревшие алгоритмы

Если вы видите хеши длиной 32 символа (MD5) или 40 символов (SHA-1), это указывает на использование устаревших алгоритмов. Они считаются небезопасными из-за высокой скорости перебора и большого количества коллизий.

Как проверить наличие и качество соли

Соль - это случайная строка, добавляемая к паролю перед хешированием. Она нужна, чтобы одинаковые пароли разных пользователей давали разные хеши. Вот как проверить наличие соли:

Для bcrypt соль встроена в хеш. Например, в хеше $2a$10$xJwL5v5z3b3Z3b3Z3b3Z3uK5v5z3b3Z3b3Z3b3Z3b3Z3b3Z3b3Z3b3Z3b3Z3 соль - это часть после стоимости.

Для SHA-256 и PBKDF2 соль обычно хранится отдельно или встроена в хеш. Например, в PBKDF2 соль может быть частью строки: pbkdf2_sha256$100000$abc123$saltedhashvalue.

Для Argon2 соль также встроена в хеш.

Если соль не используется или она одинаковая для всех пользователей, это серьёзная уязвимость. Например, если все хеши начинаются с одной и той же строки, соль отсутствует или не уникальна.

Как проверить код хеширования паролей

Даже если в базе данных пароли выглядят правильно, важно проверить реализацию в коде. Вот что нужно искать для разных языков программирования:

Python (Django, Flask)

В Django пароли хешируются автоматически при использовании стандартной модели User. Проверьте настройки хеширования в settings.py:

PASSWORD_HASHERS = [
    'django.contrib.auth.hashers.Argon2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
]

Убедитесь, что в списке нет устаревших алгоритмов вроде MD5PasswordHasher или SHA1PasswordHasher.

В Flask можно использовать библиотеку passlib. Проверьте используемый алгоритм:

from passlib.hash import bcrypt, argon2

# Хорошо:
hash = bcrypt.hash("password")
hash = argon2.hash("password")

# Плохо:
hash = sha256_crypt.hash("password")  # Без соли или с низкой стоимостью

Node.js (bcrypt, Argon2)

В Node.js часто используется библиотека bcrypt. Проверьте параметры хеширования:

const bcrypt = require('bcrypt');
const saltRounds = 10;  // Хорошо - требует значительных ресурсов
const saltRounds = 5;   // Плохо - требует мало ресурсов

bcrypt.hash("password", saltRounds, (err, hash) => {
    // ...
});

Для Argon2 проверьте параметры:

const argon2 = require('argon2');

argon2.hash("password", {
    type: argon2.argon2id,
    memoryCost: 65536,  // Хорошо - требует много памяти
    timeCost: 3,        // Хорошо - требует много времени
    parallelism: 1
});

PHP

В PHP используйте встроенную функцию password_hash():

// Хорошо:
$hash = password_hash("password", PASSWORD_BCRYPT);
$hash = password_hash("password", PASSWORD_ARGON2ID);

// Плохо:
$hash = md5("password");
$hash = sha1("password");

Как безопасно перехешировать старые пароли

Если вы обнаружили, что пароли хранятся неправильно, их нужно перехешировать. Вот как сделать это безопасно:

  1. Добавьте новый столбец для хешей в таблицу пользователей
  2. При следующем успешном входе пользователя:
    • Проверьте пароль с использованием старого алгоритма
    • Перехешируйте его с использованием нового алгоритма
    • Сохраните новый хеш в новом столбце
  3. Когда все пароли будут перехешированы, удалите старый столбец

Пример для Python (Django):

from django.contrib.auth.hashers import make_password

def migrate_passwords(user):
    if not user.password.startswith('bcrypt$') and not user.password.startswith('$2a$'):
        user.new_password_hash = make_password(user.password)
        user.save()

Как защититься от атак перебором

Даже при правильном хешировании злоумышленники могут пытаться подобрать пароли методом перебора. Вот что нужно реализовать:

Ограничение попыток входа. После нескольких неудачных попыток входа аккаунт должен блокироваться или замедляться ответ. Чем больше попыток разрешено, тем выше риск успешного подбора пароля.

CAPTCHA. Добавьте проверку на робота после нескольких неудачных попыток входа.

Двухфакторная аутентификация. Добавьте дополнительный фактор аутентификации для всех пользователей.

Пример реализации ограничения попыток входа для Django:

from django.contrib.auth import get_user_model
from django.core.cache import cache

User = get_user_model()

def login(request):
    username = request.POST.get('username')
    password = request.POST.get('password')

    # Проверяем количество попыток
    cache_key = f'login_attempts_{username}'
    attempts = cache.get(cache_key, 0)

    if attempts >= 5:
        return HttpResponse("Превышено количество попыток входа. Попробуйте позже.")

    user = authenticate(request, username=username, password=password)
    if user is not None:
        cache.delete(cache_key)
        login(request, user)
    else:
        cache.set(cache_key, attempts + 1, timeout=3600)  # Блокируем на час
        return HttpResponse("Неверные учётные данные.")

Позиция редакции: что считать достаточным уровнем защиты

Мы считаем, что для большинства приложений достаточно использовать bcrypt с высокой стоимостью вычислений или Argon2id с параметрами, требующими значительных ресурсов. Эти алгоритмы:

Специально разработаны для хеширования паролей и требуют значительных вычислительных ресурсов

Используют уникальную соль для каждого пользователя по умолчанию

Поддерживаются всеми основными языками программирования

Однако эта позиция неверна в следующих случаях:

Ваше приложение работает в среде с жёсткими ограничениями по ресурсам, где каждый вычислительный цикл критичен

Вам нужно поддерживать совместимость со старыми системами, которые не могут обновить библиотеки хеширования

Вы храните пароли не для аутентификации пользователей, а для других целей, например, для доступа к сторонним API

В таких случаях стоит рассмотреть PBKDF2 с большим количеством итераций или scrypt, но только после тщательного тестирования производительности.

Практический чек-лист для проверки сегодня

  1. Найдите таблицу с пользователями в базе данных и проверьте формат хранения паролей
  2. Определите используемый алгоритм хеширования и убедитесь в его надёжности
  3. Проверьте наличие и уникальность соли для каждого пользователя
  4. Посмотрите реализацию хеширования в коде и убедитесь в правильности выбора алгоритма
  5. Если пароли хранятся неправильно, запланируйте их перехеширование
  6. Убедитесь в наличии защиты от атак перебором: ограничение попыток входа, CAPTCHA или двухфакторная аутентификация

Если вы обнаружили уязвимости, исправьте их как можно скорее. Если всё в порядке, добавьте эти проверки в регулярный аудит безопасности вашего приложения. Пароли - первая линия обороны любой системы, и она должна быть надёжной.

С чем это связано

Метка над заголовком говорит, зачем туда идти: продолжить тему, перейти к практике или увидеть возражение.

Две угрозы по разные стороны: одна закрывается хранилищем, другая — серверомчужой скриптчужой сайт
Углубление

Токен в браузере: где его хранить

Как выбрать хранилище для токена в браузере: сравниваем защиту от разных угроз и компромиссы между ними.

Цепочка шагов конвейера CI: чужой код входит, исполняется с ключами, и последний шаг — публикация — штатно выводит секрет за границу доверияграница довериясекрет наружу
Усиление

Секреты в CI: пять типовых утечек

Как пароли утекают из CI/CD, даже если вы их прячете — пять реальных сценариев и что с ними делать.

Два стыка: «заметки и файлы» совпали, «будильник и микрофон» разошлись — список показывает, что приложение просит, но не показывает, зачемзаметки и файлыбудильник+микрофон
Просто

Почему приложение просит столько разрешений

Почему ваш телефон спрашивает доступ к камере, микрофону или контактам — и как понять, когда это оправдано, а когда нет.

Письмо по вторникам

Один разбор недели и короткий список того, что изменилось. Без дайджестов на сорок ссылок.

Начните вводить — материалы появятся здесь.

выбрать · Enter открыть · Esc закрыть