SHA-256 в Python — руководство по hashlib + примеры
Используйте бесплатный SHA-256 Hash Generator прямо в браузере — установка не требуется.
Попробовать SHA-256 Hash Generator онлайн →В каждом конвейере развёртывания рано или поздно возникает необходимость проверить контрольную сумму файла, подписать полезную нагрузку вебхука или создать ключ кеша. SHA-256 хеширование в Python с помощью встроенного модуля hashlib решает все эти задачи — и он уже установлен. hashlib.sha256() использует реализацию OpenSSL в CPython, поэтому работает быстро и поддерживает FIPS из коробки. Для быстрого одноразового хеша без написания кода воспользуйтесь онлайн-генератором SHA-256 — результат появится мгновенно. Все примеры рассчитаны на Python 3.9+.
- ✓hashlib.sha256(data).hexdigest() — стандартный способ хеширования байтов, входит в stdlib и использует OpenSSL.
- ✓Строки необходимо сначала преобразовать в байты: hashlib.sha256("text".encode("utf-8")).
- ✓Для контрольных сумм файлов передавайте данные по частям через .update() — никогда не загружайте большой файл в память целиком.
- ✓HMAC-SHA256 требует модуля hmac: hmac.new(key, msg, hashlib.sha256) — в SHA-256 самом по себе нет понятия ключа.
Что такое SHA-256 хеширование?
SHA-256 (Secure Hash Algorithm, 256 бит) принимает входные данные произвольной длины и формирует фиксированный 256-битный (32-байтовый) дайджест. Одни и те же входные данные всегда дают одинаковый результат, однако даже изменение одного бита во входных данных приводит к совершенно иному хешу — это свойство называется лавинным эффектом. SHA-256 входит в семейство SHA-2, стандартизировано NIST и лежит в основе отпечатков TLS-сертификатов, идентификаторов коммитов Git, заголовков блоков Bitcoin и верификации целостности файлов. Алгоритм использует конструкцию Меркла–Дамгора с 64 раундами сжатия для формирования 256-битного вывода.
deployment-v4.2.1
a1f7c3d8e9b2...27ae41e4649b (64 hex chars)
Показанный выше hex-дайджест — стандартное представление: 64 шестнадцатеричных символа, одинаковой длины независимо от того, хешируется один байт или целый образ диска.
hashlib.sha256() — подход стандартной библиотеки
Модуль hashlib входит в каждую установку Python — никакого pip install не требуется. Вызовите hashlib.sha256() с аргументом типа bytes для создания объекта хеша, затем получите результат через .hexdigest() (hex-строка) или .digest() (сырые байты). Имя функции пишется в нижнем регистре: sha256, а не SHA256.
import hashlib # Хеширование байтовой строки напрямую digest = hashlib.sha256(b"deployment-v4.2.1").hexdigest() print(digest) # a8f5f167f44f4964e6c998dee827110c3f1de4d0280c68cba98cf70b4b5157db
Наиболее распространённая ошибка при работе с hashlib.sha256() — передача str вместо bytes. Строки Python — это Unicode, а хеш-функции работают с сырыми байтами. Необходимо вызвать .encode("utf-8") перед хешированием. На этом спотыкаются почти все при первом знакомстве.
import hashlib
# Строки необходимо преобразовать в байты перед хешированием
config_key = "redis://cache.internal:6379/0"
digest = hashlib.sha256(config_key.encode("utf-8")).hexdigest()
print(digest)
# 7d3f8c2a1b9e4f5d6c8a7b3e2f1d9c4a5b8e7f6d3c2a1b9e4f5d6c8a7b3e2f1dМетод .update() позволяет передавать данные порционно. Вызов h.update(a); h.update(b) эквивалентен hashlib.sha256(a + b). Именно так хешируются файлы по частям без загрузки всего содержимого в память.
import hashlib h = hashlib.sha256() h.update(b"request_id=req_7f3a91bc") h.update(b"×tamp=1741614120") h.update(b"&amount=4999") print(h.hexdigest()) # Эквивалентно hashlib.sha256(b"request_id=req_7f3a91bc×tamp=1741614120&amount=4999").hexdigest()
.digest() возвращает сырые 32 байта. .hexdigest() возвращает 64-символьную hex-строку. Используйте .digest() при передаче результата в HMAC, кодирование Base64 или двоичные протоколы. Используйте .hexdigest() для логирования, колонок базы данных и сравнения контрольных сумм.HMAC-SHA256 — хеширование с ключом через модуль hmac
SHA-256 сам по себе не использует секретный ключ — любой, кто знает входные данные, может вычислить тот же хеш. Если нужно доказать, что сообщение пришло от конкретного отправителя (верификация вебхуков, подпись API-запросов, аутентификация токенов), необходим HMAC. Модуль hmac входит в стандартную библиотеку Python и встраивает ключ в процесс хеширования, так что только тот, кто знает ключ, может создать или проверить тот же дайджест.
import hmac
import hashlib
# Верификация подписи вебхука
secret_key = b"whsec_9f3a7b2e1d4c8a5b"
payload = b'{"event":"invoice.paid","invoice_id":"inv_8d2c","amount":14900}'
signature = hmac.new(secret_key, payload, hashlib.sha256).hexdigest()
print(signature)
# 64-символьный hex-дайджест HMAC-SHA256Для проверки входящего HMAC необходимо использовать hmac.compare_digest() вместо оператора ==. Оператор равенства уязвим к атакам по времени — он прерывается при первом несовпадающем байте, и злоумышленник может измерять время ответа, чтобы угадать корректную подпись побайтово. compare_digest() выполняется за постоянное время независимо от места несовпадения.
import hmac
import hashlib
def verify_webhook(payload: bytes, received_sig: str, secret: bytes) -> bool:
"""Проверяет подпись вебхука с помощью сравнения за постоянное время."""
expected = hmac.new(secret, payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received_sig)
# Имитация верификации вебхука в стиле Stripe
incoming_payload = b'{"event":"payment.completed","amount":4999}'
incoming_signature = "a1b2c3d4e5f6..." # из заголовка X-Signature
webhook_secret = b"whsec_9f3a7b2e1d4c"
if verify_webhook(incoming_payload, incoming_signature, webhook_secret):
print("Подпись верна — обрабатываем событие")
else:
print("Несовпадение подписи — отклоняем запрос")Подпись API-запросов с HMAC-SHA256
Подпись API-запросов следует тому же принципу: сформируйте каноническую строку из компонентов запроса (метод, путь, временная метка, хеш тела) и подпишите её секретным ключом. AWS Signature V4, Stripe и вебхуки GitHub — все они используют вариации этого паттерна.
import hmac
import hashlib
import time
def sign_request(method: str, path: str, body: bytes, secret: bytes) -> str:
"""Создаёт подпись HMAC-SHA256 для API-запроса."""
timestamp = str(int(time.time()))
body_hash = hashlib.sha256(body).hexdigest()
# Каноническая строка: метод + путь + временная метка + хеш тела
canonical = f"{method}\n{path}\n{timestamp}\n{body_hash}"
signature = hmac.new(secret, canonical.encode("utf-8"), hashlib.sha256).hexdigest()
return f"ts={timestamp},sig={signature}"
# Использование
api_secret = b"sk_live_9f3a7b2e1d4c8a5b6e7f"
request_body = b'{"customer_id":"cust_4f2a","plan":"enterprise"}'
auth_header = sign_request("POST", "/api/v2/subscriptions", request_body, api_secret)
print(f"Authorization: HMAC-SHA256 {auth_header}")
# Authorization: HMAC-SHA256 ts=1741614120,sig=7d3f8c2a...HMAC-SHA256 с кодированием Base64
Некоторые API (AWS Signature V4, ряд платёжных шлюзов) ожидают результат HMAC в виде строки Base64, а не hex. Разница: hex использует 64 символа, Base64 — 44 символа для того же 32-байтового дайджеста.
import hmac
import hashlib
import base64
secret = b"webhook_secret_9f3a"
message = b"POST /api/v2/events 1741614120"
# Hex-вывод: 64 символа
hex_sig = hmac.new(secret, message, hashlib.sha256).hexdigest()
print(f"Hex: {hex_sig}")
# Base64-вывод: 44 символа (короче, часто используется в HTTP-заголовках)
raw_sig = hmac.new(secret, message, hashlib.sha256).digest()
b64_sig = base64.b64encode(raw_sig).decode("ascii")
print(f"Base64: {b64_sig}")Хеширование datetime, UUID и произвольных объектов
SHA-256 работает с сырыми байтами, поэтому типы, отличные от bytes — datetime, UUID, датаклассы, модели Pydantic — необходимо сериализовать в байты перед хешированием. Автоматического преобразования нет; выбор канонического представления за вами. Для детерминированного хеширования между системами всегда используйте явную кодировку и стабильный формат сериализации (ISO 8601 для datetime, стандартную строковую форму для UUID, JSON с отсортированными ключами для словарей).
import hashlib
import uuid
from datetime import datetime, timezone
# datetime — используйте ISO 8601 с явным смещением UTC для переносимости
event_time = datetime(2026, 3, 28, 12, 0, 0, tzinfo=timezone.utc)
time_hash = hashlib.sha256(event_time.isoformat().encode("utf-8")).hexdigest()
print(f"datetime hash: {time_hash[:16]}...")
# UUID — хешируем каноническую строковую форму (нижний регистр, с дефисами)
record_id = uuid.uuid4()
uuid_hash = hashlib.sha256(str(record_id).encode("utf-8")).hexdigest()
print(f"UUID hash: {uuid_hash[:16]}...")Для произвольных объектов сериализуйте их в каноническое байтовое представление перед хешированием. JSON с отсортированными ключами хорошо подходит для словарей и подобных объектов:
import hashlib
import json
from dataclasses import dataclass, asdict
@dataclass
class Event:
id: str
type: str
amount: int
timestamp: str
def hash_event(event: Event) -> str:
"""Хеширует датакласс, используя JSON с отсортированными ключами для детерминизма."""
canonical = json.dumps(asdict(event), sort_keys=True, separators=(",", ":"))
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
e = Event(id="evt_4f2a", type="payment.completed", amount=4999, timestamp="2026-03-28T12:00:00Z")
print(hash_event(e)) # стабильный результат между запусками и машинамиsort_keys=True) при хешировании JSON-сериализованных объектов. Порядок вставки в словарь сохраняется в Python 3.7+, но может различаться в зависимости от пути сериализации, что даст разные хеши для идентичных данных.Контрольная сумма SHA-256 файла — верификация загрузок и артефактов
Вычисление контрольной суммы SHA-256 файла — одно из наиболее распространённых применений алгоритма. Вы встречаете его повсюду: страницы релизов бинарных файлов Go, wheel-файлы Python, манифесты образов Docker, обновления прошивки. Ключевое правило — читать файл по частям, а не загружать целиком: для хеширования ISO-образа объёмом 2 ГБ не должно требоваться 2 ГБ оперативной памяти.
import hashlib
def sha256_checksum(filepath: str, chunk_size: int = 8192) -> str:
"""Вычисляет SHA-256 хеш файла, читая по частям для экономии памяти."""
h = hashlib.sha256()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(chunk_size), b""):
h.update(chunk)
return h.hexdigest()
# Хешируем артефакт релиза
checksum = sha256_checksum("/tmp/release-v4.2.1.tar.gz")
print(f"SHA-256: {checksum}")В Python 3.11 добавлена функция hashlib.file_digest(), которая выполняет порционное чтение внутренне и может использовать оптимизации zero-copy на поддерживаемых платформах. Если вы работаете на Python 3.11 или новее, отдайте предпочтение ей перед ручным циклом.
import hashlib
with open("/tmp/release-v4.2.1.tar.gz", "rb") as f:
digest = hashlib.file_digest(f, "sha256")
print(digest.hexdigest())Проверка загруженного файла по известной контрольной сумме
import hashlib
import hmac as hmac_mod # только для compare_digest
def verify_checksum(filepath: str, expected_hex: str) -> bool:
"""Проверяет контрольную сумму SHA-256 с использованием сравнения за постоянное время."""
h = hashlib.sha256()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
return hmac_mod.compare_digest(h.hexdigest(), expected_hex.lower())
# Проверяем артефакт релиза
expected = "a8f5f167f44f4964e6c998dee827110c3f1de4d0280c68cba98cf70b4b5157db"
if verify_checksum("/tmp/release-v4.2.1.tar.gz", expected):
print("Контрольные суммы совпадают — файл не повреждён")
else:
print("Несовпадение контрольных сумм — файл может быть повреждён или подменён")hmac.compare_digest() для сравнения контрольных сумм, даже если нет секретного ключа. Сравнение за постоянное время предотвращает утечку информации через измерение времени. Оператор == функционально корректен, но небезопасен в контекстах, чувствительных к безопасности.SHA-256 с кодированием Base64
Некоторые протоколы ожидают дайджест SHA-256 в виде строки Base64, а не hex. HTTP-заголовки Content-Digest и Integrity (Subresource Integrity в браузерах) используют Base64, а подписи JWT кодируются в Base64url. Важно применять Base64-кодирование к сырым байтам из .digest(), а не к hex-строке.
import hashlib
import base64
data = b"integrity check payload"
# Правильно: Base64 от сырых байтов (32 байта → 44 символа Base64)
raw_digest = hashlib.sha256(data).digest()
b64_digest = base64.b64encode(raw_digest).decode("ascii")
print(f"sha256-{b64_digest}")
# sha256-<44 символа>
# Неправильно: Base64 от hex-строки (64 ASCII-байта → 88 символов Base64 — вдвое больше)
hex_digest = hashlib.sha256(data).hexdigest()
wrong = base64.b64encode(hex_digest.encode()).decode()
print(f"Неверная длина: {len(wrong)} символов") # 88 — не то, что ожидают API.digest(), а не .hexdigest(), перед кодированием в Base64.Справочник hashlib.sha256()
Конструктор и методы объекта хеша SHA-256:
Параметры hmac.new() для хеширования с ключом:
Библиотека cryptography — альтернативный API для SHA-256
Пакет cryptography предоставляет другой API для SHA-256 через низкоуровневые примитивы hazmat. Когда нужен просто хеш, я редко к нему обращаюсь — hashlib проще и не требует внешних зависимостей. Но если ваш проект уже использует cryptography для TLS, X.509 или симметричного шифрования, применение его хеш-API сохраняет единообразие в рамках одной библиотеки и обеспечивает согласованную обработку ошибок.
from cryptography.hazmat.primitives import hashes from cryptography.hazmat.backends import default_backend digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) digest.update(b"deployment-v4.2.1") result = digest.finalize() # сырые 32 байта print(result.hex()) # 64-символьная hex-строка # a8f5f167f44f4964e6c998dee827110c3f1de4d0280c68cba98cf70b4b5157db
cryptography требует pip install cryptography. Объект хеша одноразовый: повторный вызов .finalize() вызывает исключение AlreadyFinalized. Используйте .copy() перед финализацией, если нужно разветвить состояние хеша.Хеширование данных из файла и ответа API
Два сценария встречаются постоянно: хеширование файла на диске для верификации артефакта релиза и хеширование тела HTTP-ответа для использования в качестве ключа кеша или верификации вебхука.
Чтение файла → вычисление SHA-256 → сравнение
import hashlib
import sys
def hash_file_safe(filepath: str) -> str | None:
"""Хеширует файл с корректной обработкой ошибок."""
try:
h = hashlib.sha256()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(16384), b""):
h.update(chunk)
return h.hexdigest()
except FileNotFoundError:
print(f"Ошибка: файл {filepath} не найден", file=sys.stderr)
return None
except PermissionError:
print(f"Ошибка: нет прав на чтение {filepath}", file=sys.stderr)
return None
result = hash_file_safe("/etc/nginx/nginx.conf")
if result:
print(f"SHA-256: {result}")HTTP-ответ → хеширование тела для ключа кеша
import hashlib
import urllib.request
import json
def fetch_and_hash(url: str) -> tuple[dict, str]:
"""Загружает JSON из API и возвращает данные вместе с SHA-256 хешем."""
try:
with urllib.request.urlopen(url, timeout=10) as resp:
body = resp.read()
content_hash = hashlib.sha256(body).hexdigest()
data = json.loads(body)
return data, content_hash
except urllib.error.URLError as exc:
raise ConnectionError(f"Не удалось получить данные с {url}: {exc}") from exc
# Ключ кеша на основе содержимого ответа
data, digest = fetch_and_hash("https://api.exchange.internal/v2/rates")
print(f"Хеш ответа: {digest[:16]}...")
print(f"EUR/USD: {data.get('rates', {}).get('EUR', 'N/A')}")Для быстрой одноразовой проверки генератор SHA-256 от ToolDeck работает полностью в браузере — без написания кода.
SHA-256 хеширование в командной строке
Иногда во время инцидента или развёртывания нужно быстро вычислить хеш прямо в терминале. Модуль hashlib не имеет встроенной подкоманды CLI (в отличие от python3 -m json.tool), но можно воспользоваться однострочником или системными утилитами.
# Однострочник Python echo -n "deployment-v4.2.1" | python3 -c "import hashlib,sys; print(hashlib.sha256(sys.stdin.buffer.read()).hexdigest())" # macOS / BSD echo -n "deployment-v4.2.1" | shasum -a 256 # Linux (coreutils) echo -n "deployment-v4.2.1" | sha256sum # OpenSSL (кроссплатформенный) echo -n "deployment-v4.2.1" | openssl dgst -sha256
# Хешируем архив релиза sha256sum release-v4.2.1.tar.gz # или openssl dgst -sha256 release-v4.2.1.tar.gz # Проверяем по известной контрольной сумме echo "a8f5f167f44f4964e6c998dee827110c release-v4.2.1.tar.gz" | sha256sum -c - # release-v4.2.1.tar.gz: OK
echo -n (без завершающего символа новой строки) при хешировании строк в командной строке. Обычный echo добавляет \n, что изменяет хеш. Это главная причина, по которой результаты Python и оболочки расходятся.Высокопроизводительные варианты — hashlib с OpenSSL и pycryptodome
В CPython hashlib.sha256() уже делегирует вычисления реализации OpenSSL на C, поэтому работает быстро — как правило, более 500 МБ/с на современном оборудовании.
Если SHA-256 хеширование появляется в профилировщике — например, вы вычисляете контрольные суммы тысяч файлов в CI-конвейере или хешируете тело каждого запроса в высоконагруженном API-шлюзе — есть два варианта: оптимизировать паттерн вызова hashlib или переключиться на pycryptodome — унифицированный криптографический API, охватывающий также SHA-3 и BLAKE2:
pip install pycryptodome
from Crypto.Hash import SHA256 h = SHA256.new() h.update(b"deployment-v4.2.1") print(h.hexdigest()) # a8f5f167f44f4964e6c998dee827110c3f1de4d0280c68cba98cf70b4b5157db
При параллельном хешировании большого количества файлов наибольший выигрыш даёт снижение накладных расходов Python за счёт увеличения размера блока и многопоточности:
import hashlib
import os
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
def hash_file(path: Path) -> tuple[str, str]:
"""Хеширует один файл и возвращает (путь, hex-дайджест)."""
h = hashlib.sha256()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(65536), b""): # блоки по 64 КБ
h.update(chunk)
return str(path), h.hexdigest()
def hash_directory(directory: str, pattern: str = "*.tar.gz") -> dict[str, str]:
"""Хеширует все подходящие файлы параллельно с использованием потоков."""
files = list(Path(directory).glob(pattern))
results = {}
with ThreadPoolExecutor(max_workers=os.cpu_count()) as pool:
for path, digest in pool.map(hash_file, files):
results[path] = digest
return results
# Хешируем все артефакты релизов параллельно
checksums = hash_directory("/var/releases", "*.tar.gz")
for path, digest in checksums.items():
print(f"{digest} {path}")Использование блоков по 64 КБ вместо 8 КБ сокращает количество вызовов из Python в C в 8 раз. Потоки хорошо подходят здесь, поскольку GIL освобождается во время C-уровневого хеширования — узким местом является дисковый ввод-вывод, а не процессор.
Вывод в терминал с цветовым выделением
Библиотека rich удобна, когда нужно проверить пакет файлов и отобразить таблицу со статусом прохождения/отказа для каждого файла, а не листать мелькающие hex-строки в терминале.
pip install rich
import hashlib
from pathlib import Path
from rich.console import Console
from rich.table import Table
console = Console()
def hash_and_display(files: list[str], expected: dict[str, str]) -> None:
"""Хеширует файлы и отображает результаты с цветовой индикацией статуса."""
table = Table(title="Верификация SHA-256")
table.add_column("Файл", style="cyan")
table.add_column("SHA-256", style="dim", max_width=20)
table.add_column("Статус")
for filepath in files:
h = hashlib.sha256()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
digest = h.hexdigest()
name = Path(filepath).name
status = "[green]✓ OK[/green]" if expected.get(name) == digest else "[red]✗ НЕСОВПАДЕНИЕ[/red]"
table.add_row(name, f"{digest[:16]}...", status)
console.print(table)
# Использование
expected_checksums = {
"api-gateway-v3.1.tar.gz": "a8f5f167f44f4964...",
"worker-v3.1.tar.gz": "7d3f8c2a1b9e4f5d...",
}
hash_and_display(
["/var/releases/api-gateway-v3.1.tar.gz", "/var/releases/worker-v3.1.tar.gz"],
expected_checksums,
)console.print(data, highlight=False) или перенаправьте в файл через Console(file=open(...)).Работа с большими файлами
Порционный паттерн с .update() обрабатывает файлы любого размера при постоянном потреблении памяти. Для очень больших файлов (многогигабайтные образы дисков, резервные копии баз данных) основная забота смещается от памяти к обратной связи с пользователем — хеширование 10 ГБ при 500 МБ/с занимает 20 секунд, и тишина в это время нервирует.
import hashlib
import os
def sha256_with_progress(filepath: str) -> str:
"""Хеширует большой файл с отображением прогресса в stderr."""
file_size = os.path.getsize(filepath)
h = hashlib.sha256()
bytes_read = 0
with open(filepath, "rb") as f:
while chunk := f.read(1 << 20): # блоки по 1 МБ
h.update(chunk)
bytes_read += len(chunk)
pct = (bytes_read / file_size) * 100
print(f"\r Хеширование: {pct:.1f}% ({bytes_read >> 20} МБ / {file_size >> 20} МБ)",
end="", flush=True)
print() # перевод строки после прогресса
return h.hexdigest()
digest = sha256_with_progress("/mnt/backups/db-snapshot-2026-03.sql.gz")
print(f"SHA-256: {digest}")NDJSON / JSON Lines — хеширование каждой записи отдельно
import hashlib
import json
def hash_ndjson_records(filepath: str) -> dict[str, str]:
"""Хеширует каждую JSON-запись в NDJSON-файле для дедупликации."""
seen = {}
with open(filepath, "r", encoding="utf-8") as f:
for line_num, line in enumerate(f, 1):
line = line.strip()
if not line:
continue
try:
record = json.loads(line)
# Нормализуем перед хешированием: сортируем ключи для детерминизма
canonical = json.dumps(record, sort_keys=True, separators=(",", ":"))
digest = hashlib.sha256(canonical.encode("utf-8")).hexdigest()
if digest in seen:
print(f"Строка {line_num}: дубликат строки {seen[digest]}")
else:
seen[digest] = line_num
except json.JSONDecodeError:
print(f"Строка {line_num}: некорректный JSON, пропущена")
print(f"Обработано {line_num} строк, {len(seen)} уникальных записей")
return seen
hash_ndjson_records("telemetry-events-2026-03.ndjson")hashlib.sha256(data) на порционный цикл с .update(), когда файлы превышают 50–100 МБ. Ниже этого порога чтение всего файла через f.read() допустимо — потребление памяти будет примерно равно размеру файла.Распространённые ошибки
Проблема: hashlib.sha256('text') вызывает TypeError: Unicode-objects must be encoded before hashing. Функция требует bytes, а не str.
Решение: Закодируйте строку сначала: hashlib.sha256('text'.encode('utf-8')). Или используйте литерал b'' для жёстко заданных значений.
import hashlib
digest = hashlib.sha256("deployment-v4.2.1").hexdigest()
# TypeError: Unicode-objects must be encoded before hashingimport hashlib
digest = hashlib.sha256("deployment-v4.2.1".encode("utf-8")).hexdigest()
# Работает — возвращает 64-символьную hex-строкуПроблема: Оператор == прерывается при первом несовпадающем байте. Злоумышленник может измерять время ответа и угадывать правильную подпись побайтово.
Решение: Используйте hmac.compare_digest() для всех сравнений, чувствительных к безопасности — он выполняется за постоянное время независимо от места несовпадения.
received_sig = request.headers["X-Signature"]
expected_sig = hmac.new(key, body, hashlib.sha256).hexdigest()
if received_sig == expected_sig: # уязвимо к атаке по времени
process_webhook(body)received_sig = request.headers["X-Signature"]
expected_sig = hmac.new(key, body, hashlib.sha256).hexdigest()
if hmac.compare_digest(received_sig, expected_sig): # постоянное время
process_webhook(body)Проблема: base64.b64encode(digest.hexdigest().encode()) даёт 88-символьную строку — вдвое больше ожидаемых 44 символов. API, ожидающие Base64-кодированный SHA-256, отклонят такой результат.
Решение: Вызывайте .digest() (сырые байты) перед кодированием в Base64, а не .hexdigest() (hex-строка).
import hashlib, base64 hex_str = hashlib.sha256(data).hexdigest() b64 = base64.b64encode(hex_str.encode()) # 88 символов — неправильно!
import hashlib, base64 raw = hashlib.sha256(data).digest() b64 = base64.b64encode(raw) # 44 символа — правильно
Проблема: hashlib.sha256(open('large.iso', 'rb').read()) загружает весь файл в память. Для файла 4 ГБ только для вычисления хеша потребуется 4 ГБ оперативной памяти.
Решение: Читайте по частям в цикле с .update(). Потребление памяти остаётся постоянным независимо от размера файла.
import hashlib
# Загружает весь файл 4 ГБ в память
digest = hashlib.sha256(open("disk.iso", "rb").read()).hexdigest()import hashlib
h = hashlib.sha256()
with open("disk.iso", "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
digest = h.hexdigest() # постоянное потребление памятиhashlib vs hmac vs альтернативы — краткое сравнение
Для простого хеширования — контрольные суммы, ключи кеша, отпечатки содержимого — используйте hashlib.sha256(). Переходите на hmac.new() как только потребуется секретный ключ (вебхуки, подписи API, аутентификация токенов). Обращайтесь к библиотеке cryptography только если проект уже использует её для шифрования или TLS — добавлять зависимость от C-расширения только ради хеширования избыточно, когда hashlib уже использует OpenSSL.
Можно ли расшифровать SHA-256? — хеширование против шифрования
Короткий ответ: нет. SHA-256 — это односторонняя функция. Алгоритм спроектирован необратимым — восстановить исходные входные данные из 256-битного дайджеста невозможно. Это не ограничение реализации, а математическое свойство хеш-функции. Пространство 256-битного вывода астрономически велико (2256 возможных значений), и функция теряет информацию в процессе 64 раундов сжатия.
Злоумышленники могут применять перебор или атаки по словарю против слабых входных данных (распространённые пароли, короткие строки), однако для любых входных данных с достаточной энтропией — API-ключи, случайные токены, содержимое файлов — обращение SHA-256 вычислительно невозможно на современном оборудовании. Если нужно обратимое преобразование, используйте симметричное шифрование:
# Хеширование — одностороннее, исходные данные не восстановить import hashlib digest = hashlib.sha256(b"secret-config-value").hexdigest() # Нет способа получить "secret-config-value" из дайджеста # Шифрование — двустороннее, можно расшифровать с ключом from cryptography.fernet import Fernet key = Fernet.generate_key() cipher = Fernet(key) encrypted = cipher.encrypt(b"secret-config-value") decrypted = cipher.decrypt(encrypted) print(decrypted) # b"secret-config-value" — исходные данные восстановлены
Для быстрого способа сгенерировать SHA-256 хеш без установки — онлайн-инструмент работает полностью в браузере.
Как проверить, является ли строка корректным SHA-256 хешем в Python
Корректный hex-дайджест SHA-256 состоит ровно из 64 шестнадцатеричных символов (0-9, a-f, A-F). Быстрая проверка перед обработкой недоверенных входных данных предотвращает запутанные ошибки на последующих этапах.
import re
def is_sha256_hex(value: str) -> bool:
"""Проверяет, соответствует ли строка формату hex-дайджеста SHA-256."""
return bool(re.fullmatch(r"[a-fA-F0-9]{64}", value))
# Тестовые случаи
print(is_sha256_hex("e3b0c44298fc1c149afbf4c8996fb924"
"27ae41e4649b934ca495991b7852b855")) # True — SHA-256 пустой строки
print(is_sha256_hex("e3b0c44298fc1c14")) # False — слишком короткая
print(is_sha256_hex("zzzz" * 16)) # False — недопустимые hex-символыЧасто задаваемые вопросы
Как захешировать строку с помощью SHA-256 в Python?
Вызовите hashlib.sha256() со строкой, закодированной в байты. Строки в Python — это Unicode, а хеш-функции работают с сырыми байтами, поэтому сначала необходимо вызвать .encode("utf-8"). Метод .hexdigest() возвращает привычную 64-символьную hex-строку.
import hashlib
api_key = "sk_live_9f3a7b2e1d4c"
digest = hashlib.sha256(api_key.encode("utf-8")).hexdigest()
print(digest)
# e3b7c4a1f8d2...64 hex charactersМожно ли расшифровать хеш SHA-256 обратно в исходный текст?
Нет. SHA-256 — это односторонняя функция: она преобразует входные данные произвольной длины в фиксированный 256-битный вывод и в процессе теряет структуру исходных данных. Математической обратной функции не существует. Злоумышленники могут пытаться применить перебор или таблицы радужных хешей против слабых входных данных (короткие пароли, распространённые слова), но для любых входных данных с достаточной энтропией обращение SHA-256 вычислительно невозможно. Если вам нужно обратимое преобразование, используйте шифрование (AES-GCM, Fernet), а не хеширование.
В чём разница между .digest() и .hexdigest()?
.digest() возвращает сырые 32 байта хеша в виде объекта bytes. .hexdigest() возвращает те же данные, закодированные в виде 64-символьной строки в нижнем регистре hex. Используйте .digest(), когда нужен двоичный вывод — для передачи в HMAC, кодирования Base64 или записи в двоичные протоколы. Используйте .hexdigest(), когда нужна читаемая строка для логирования, хранения в базе данных или сравнения контрольных сумм.
import hashlib h = hashlib.sha256(b"deployment-v4.2.1") print(len(h.digest())) # 32 (bytes) print(len(h.hexdigest())) # 64 (hex characters)
Как вычислить контрольную сумму SHA-256 файла в Python?
Откройте файл в двоичном режиме и передавайте его в хешер по частям через .update(). В Python 3.11+ используйте hashlib.file_digest() для более простого API. Никогда не вызывайте f.read() для больших файлов — это загружает весь файл в память.
import hashlib
def sha256_file(path: str) -> str:
h = hashlib.sha256()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
return h.hexdigest()
print(sha256_file("release-v4.2.1.tar.gz"))Как создать подпись HMAC-SHA256 в Python?
Используйте модуль hmac с hashlib.sha256 в качестве digestmod. Передайте секретный ключ и сообщение в виде байтов. Результат — хеш с ключом, который доказывает как целостность, так и подлинность: получатель должен иметь тот же ключ для проверки.
import hmac
import hashlib
secret = b"webhook_secret_9f3a"
payload = b'{"event":"payment.completed","amount":4999}'
signature = hmac.new(secret, payload, hashlib.sha256).hexdigest()
print(signature) # 64-char hex HMACКак проверить, является ли строка корректным hex-дайджестом SHA-256?
Hex-дайджест SHA-256 состоит ровно из 64 шестнадцатеричных символов. Используйте регулярное выражение или простую проверку длины и состава символов. Подход с регулярным выражением наиболее читаем.
import re
def is_sha256(s: str) -> bool:
return bool(re.fullmatch(r"[a-fA-F0-9]{64}", s))
print(is_sha256("e3b0c44298fc1c149afbf4c8996fb924"
"27ae41e4649b934ca495991b7852b855")) # True
print(is_sha256("not-a-hash")) # FalseПохожие инструменты
Dmitri is a DevOps engineer who relies on Python as his primary scripting and automation language. He builds internal tooling, CI/CD pipelines, and infrastructure automation scripts that run in production across distributed teams. He writes about the Python standard library, subprocess management, file processing, encoding utilities, and the practical shell-adjacent Python that DevOps engineers use every day.
Maria is a backend developer specialising in Python and API integration. She has broad experience with data pipelines, serialisation formats, and building reliable server-side services. She is an active member of the Python community and enjoys writing practical, example-driven guides that help developers solve real problems without unnecessary theory.