Хеши vs Шифрование vs Хеширование паролей: Что нужно знать разработчикам
Вам нужно проверить, что скачанный файл не был повреждён, безопасно хранить пароли пользователей или шифровать конфиденциальные данные. Выбор неправильного алгоритма— использование MD5 для паролей или SHA256 для шифрования—создаёт серьёзные уязвимости безопасности. Это руководство объясняет, что на самом деле делают хеширование, шифрование и хеширование паролей, и когда использовать каждый метод.
Три категории: Что они на самом деле делают
1. Криптографическое хеширование (MD5, SHA-1, SHA-256, SHA-512)
Назначение: Генерация отпечатка данных фиксированного размера.
- Односторонний: Невозможно обратить хеш, чтобы получить исходные данные
- Детерминированный: Одинаковый вход всегда производит одинаковый выход
- Быстрый: Разработан для скорости
- Устойчивый к коллизиям: Сложно найти два входа с одинаковым хешем (в теории)
Распространённое использование: Проверка целостности файлов, контрольные суммы, цифровые подписи, дедупликация данных
2. Шифрование (AES, RSA, ChaCha20)
Назначение: Преобразование данных так, чтобы только авторизованные стороны могли их прочитать.
- Двусторонний: Можно шифровать и расшифровывать с помощью ключа(ей)
- Конфиденциальность: Защищает данные при передаче или хранении
- Зависит от ключа: Безопасность зависит от секретности ключа
- Симметричное или асимметричное: Один ключ (AES) или пара ключей (RSA)
Распространённое использование: HTTPS, шифрование баз данных, шифрование файлов, защищённый обмен сообщениями
3. Хеширование паролей (bcrypt, scrypt, Argon2, PBKDF2)
Назначение: Безопасное хранение паролей для сопротивления атакам грубой силы.
- Односторонний: Невозможно обратить, чтобы получить пароль
- Медленный по дизайну: Делает перебор дорогим
- С солью: Случайная соль предотвращает атаки с радужными таблицами
- Адаптивный: Можно увеличивать фактор стоимости со временем
Распространённое использование: Хранение паролей пользователей, деривация API ключей
Когда использовать криптографическое хеширование
Проверка целостности файлов
Проверка того, что скачанные файлы не были повреждены или изменены. Генератор хешей/контрольных сумм может вычислять MD5, SHA-1, SHA-256 и SHA-512 хеши для текстового ввода.
# Скачать файл и его опубликованный SHA-256 хеш
curl -O https://example.com/software.tar.gz
curl -O https://example.com/software.tar.gz.sha256
# Вычислить хеш локально
sha256sum software.tar.gz
# Вывод: a3b5c7d9e1f2... software.tar.gz
# Сравнить с опубликованным хешем
cat software.tar.gz.sha256
# Вывод: a3b5c7d9e1f2...
# Если хеши совпадают, файл подлинный и не повреждёнКакой алгоритм использовать:
- MD5: Быстрый, но криптографически взломанный. Используйте только для некритичных к безопасности целей (контрольные суммы, дедупликация)
- SHA-1: Устаревший из-за атак коллизий. Избегайте для критичных к безопасности приложений
- SHA-256: Стандартный выбор для большинства приложений. Хороший баланс скорости и безопасности
- SHA-512: Более сильный вариант. Используйте, когда нужен дополнительный запас безопасности
ID коммитов Git
Git использует SHA-1 хеши для идентификации коммитов, деревьев и блобов. Каждый хеш коммита детерминирован на основе содержимого, родительских коммитов, автора и временной метки:
git log --oneline
a3b5c7d Fix authentication bug
e1f2g3h Add user profile endpoint
i4j5k6l Update dependenciesGit переходит на SHA-256 из-за уязвимостей коллизий SHA-1, но SHA-1 остаётся широко используемым.
Дедупликация данных
Идентификация дубликатов файлов путём сравнения хешей:
import hashlib
def hash_file(filepath):
sha256 = hashlib.sha256()
with open(filepath, 'rb') as f:
for chunk in iter(lambda: f.read(8192), b''):
sha256.update(chunk)
return sha256.hexdigest()
# Найти дубликаты
seen_hashes = {}
for file in files:
file_hash = hash_file(file)
if file_hash in seen_hashes:
print(f"Дубликат: {file} == {seen_hashes[file_hash]}")
else:
seen_hashes[file_hash] = fileETag заголовки в HTTP
Веб-серверы используют хеши для валидации кэша:
HTTP/1.1 200 OK
Content-Type: text/html
ETag: "a3b5c7d9e1f2"
Last-Modified: Mon, 19 Jan 2026 10:00:00 GMT
# Клиент включает ETag в последующий запрос
GET /index.html HTTP/1.1
If-None-Match: "a3b5c7d9e1f2"
# Сервер отвечает 304 Not Modified если содержимое не изменилосьКогда использовать шифрование
HTTPS/TLS коммуникация
Веб-браузеры используют шифрование для защиты данных при передаче. HTTPS комбинирует асимметричное шифрование (RSA/ECDSA для обмена ключами) с симметричным шифрованием (AES для основных данных):
# Браузер подключается к серверу
Client: [ClientHello] - Поддерживаемые cipher suites
Server: [ServerHello] - Выбранный cipher (например, TLS_AES_256_GCM_SHA384)
Server: [Certificate] - Публичный ключ сервера
Client: [ClientKeyExchange] - Зашифрованный сеансовый ключ
Both: [ChangeCipherSpec] - Переключение на зашифрованную коммуникацию
# Все последующие данные зашифрованы с AES-256Шифрование базы данных в покое
Защита конфиденциальных данных, хранящихся в базах данных:
# Шифровать чувствительные поля перед сохранением
from cryptography.fernet import Fernet
key = Fernet.generate_key() # Хранить безопасно (не в коде!)
cipher = Fernet(key)
# Шифрование
plaintext = b"user@example.com"
ciphertext = cipher.encrypt(plaintext)
# Сохранить: gAAAAABh3b5c... в базе данных
# Расшифровка при необходимости
retrieved = cipher.decrypt(ciphertext)
# Вывод: b"user@example.com"Управление ключами критично: Потеря ключа шифрования означает постоянную потерю данных. Храните ключи в безопасных системах управления ключами (AWS KMS, HashiCorp Vault и т.д.).
Шифрование файлов
Шифрование файлов перед хранением в облаке или на диске:
# Использование GPG для шифрования файла
gpg --symmetric --cipher-algo AES256 document.pdf
# Создаёт: document.pdf.gpg
# Расшифровка
gpg --decrypt document.pdf.gpg > document.pdfКогда использовать хеширование паролей
Хранение паролей пользователей
Никогда не храните пароли в открытом виде и не используйте быстрые хеши типа SHA-256. Используйте алгоритмы хеширования паролей, разработанные для сопротивления атакам грубой силы:
# НЕПРАВИЛЬНО: Быстрый хеш (уязвим к перебору)
import hashlib
password = "user_password"
hash = hashlib.sha256(password.encode()).hexdigest()
# Атакующий может тестировать миллиарды паролей в секунду
# ПРАВИЛЬНО: bcrypt (медленный по дизайну)
import bcrypt
password = "user_password"
salt = bcrypt.gensalt(rounds=12) # Фактор стоимости
hashed = bcrypt.hashpw(password.encode(), salt)
# Вывод: $2b$12$abcdef... (включает соль и стоимость)
# Проверка пароля
if bcrypt.checkpw(user_input.encode(), hashed):
print("Пароль правильный")
else:
print("Пароль неправильный")Почему bcrypt/scrypt/Argon2?
- Намеренно медленный: Требует ~100мс для вычисления, ограничивая атакующего тысячами (не миллиардами) попыток в секунду
- Автоматическая соль: Каждый пароль получает уникальную случайную соль для предотвращения атак с радужными таблицами
- Адаптивная стоимость: Увеличивайте раунды/стоимость по мере улучшения аппаратного обеспечения
Деривация API ключей
Генерация API ключей из мастер-секретов:
from hashlib import pbkdf2_hmac
master_secret = b"master_key_here"
salt = b"api_key_salt_v1"
iterations = 100000
api_key = pbkdf2_hmac('sha256', master_secret, salt, iterations)
print(api_key.hex())
# Вывод: a3b5c7d9e1f2...Распространённые ошибки, которых следует избегать
1. Использование MD5 или SHA-256 для паролей
# ПЛОХО: Быстрый хеш можно взломать перебором
password_hash = hashlib.md5("password123".encode()).hexdigest()
# Атакующий может тестировать 1 миллиард MD5 хешей в секунду на GPU
# "password123" будет взломан мгновенноИсправление: Используйте bcrypt, scrypt или Argon2 с подходящими факторами стоимости.
2. Использование шифрования, когда нужно хеширование
# ПЛОХО: Проверка целостности файла с использованием шифрования
encrypted_content = aes_encrypt(file_content, key)
# Проблема: Любой с ключом может подделать "валидное" содержимое
# ХОРОШО: Проверка целостности файла с использованием хеша
file_hash = hashlib.sha256(file_content).hexdigest()
# Ключ не нужен; хеш доказывает, что содержимое не изменилось3. Неиспользование соли для паролей
# ПЛОХО: Одинаковый пароль = одинаковый хеш
user1_hash = bcrypt.hashpw("password123".encode(), bcrypt.gensalt())
user2_hash = bcrypt.hashpw("password123".encode(), bcrypt.gensalt())
# Разные хеши благодаря разным солям (ХОРОШО)
# Если вы вручную контролируете соль:
# ПЛОХО: Переиспользование одной соли для всех паролей
salt = bcrypt.gensalt() # Сгенерирована один раз
user1_hash = bcrypt.hashpw("password123".encode(), salt)
user2_hash = bcrypt.hashpw("password123".encode(), salt)
# Одинаковый хеш! Атакующий может взломать один пароль и найти всех пользователей с тем же паролемИсправление: Позвольте bcrypt/scrypt генерировать уникальную соль для каждого пароля (автоматически при правильном использовании).
4. Низкий фактор стоимости для хеширования паролей
# ПЛОХО: Слишком мало раундов (быстро = небезопасно)
salt = bcrypt.gensalt(rounds=4) # Слишком низко!
# Время вычисления: ~1мс на хеш
# ХОРОШО: Подходящий фактор стоимости
salt = bcrypt.gensalt(rounds=12) # Стандарт для 2026
# Время вычисления: ~100-200мс на хеш
# Увеличивайте до 13-14 по мере улучшения аппаратного обеспечения5. Жёсткое кодирование ключей шифрования
# ПЛОХО: Ключ в исходном коде
encryption_key = "a3b5c7d9e1f2..." # Любой с доступом к коду может расшифровать
# ХОРОШО: Загрузка из безопасного хранилища
import os
encryption_key = os.environ['ENCRYPTION_KEY'] # Из менеджера секретовДерево решений: Что мне использовать?
Нужно...
├─ Проверить, что файл не изменился?
│ └─ Используйте: SHA-256 хеш (криптографическое хеширование)
│ Инструмент: Генератор хешей/контрольных сумм
│
├─ Хранить пароли пользователей?
│ └─ Используйте: bcrypt/Argon2 (хеширование паролей)
│ НИКОГДА не используйте: MD5, SHA-256 или любой быстрый хеш
│
├─ Скрыть данные так, чтобы только авторизованные пользователи могли их прочитать?
│ └─ Используйте: AES-256 шифрование
│ Нужно расшифровать позже? Да → Шифрование
│
├─ Идентифицировать дубликаты файлов?
│ └─ Используйте: MD5 или SHA-256 хеш (контрольные суммы)
│
├─ Сгенерировать ID коммита или отпечаток содержимого?
│ └─ Используйте: SHA-256 хеш (детерминированный отпечаток)
│
└─ Защитить данные при передаче (API, HTTPS)?
└─ Используйте: TLS/SSL шифрование (обрабатывается автоматически)Практические инструменты
Для быстрой генерации хешей:
- Генератор хешей/контрольных сумм - Вычисление MD5, SHA-1, SHA-256, SHA-512 хешей для текста
- Серверный калькулятор хешей - Корпоративная генерация хешей для больших файлов с высокой производительностью
Инструменты командной строки:
# Сгенерировать SHA-256 хеш
echo -n "test content" | sha256sum
echo -n "test content" | openssl dgst -sha256
# Сгенерировать контрольную сумму файла
sha256sum file.txt
md5sum file.txt
# bcrypt пароль (требуется инструмент bcrypt)
htpasswd -bnBC 12 "" your_password | tr -d ':
'Лучшие практики безопасности
- Для паролей: Всегда используйте bcrypt (rounds≥12), scrypt или Argon2. Никогда не используйте MD5, SHA-1 или SHA-256.
- Для целостности файлов: Используйте SHA-256. Избегайте MD5 и SHA-1 для критичной к безопасности проверки.
- Для шифрования: Используйте AES-256 (симметричное) или RSA-2048+ (асимметричное). Никогда не реализуйте собственное шифрование.
- Управление ключами: Храните ключи шифрования в безопасных системах управления ключами, не в коде или конфигурационных файлах.
- Солите пароли: Всегда используйте уникальную соль для каждого пароля (bcrypt делает это автоматически).
- Пересматривайте алгоритмы: MD5 и SHA-1 устарели для использования в безопасности. Переходите на SHA-256+.
Резюме
- Криптографическое хеширование (MD5, SHA-256): Быстрые, односторонние отпечатки для целостности файлов, контрольных сумм и дедупликации
- Шифрование (AES, RSA): Двусторонняя защита конфиденциальных данных при передаче или хранении
- Хеширование паролей (bcrypt, Argon2): Медленная, с солью, односторонняя защита от атак грубой силы на пароли
Используйте Генератор хешей/контрольных сумм для быстрого хеширования текста или Серверный калькулятор хешей для высокопроизводительного хеширования файлов. Выбирайте правильный алгоритм для вашего случая использования—от этого зависит безопасность.