Python SHA-256哈希 — hashlib

·DevOps Engineer & Python Automation Specialist·审阅者Maria Santos·发布日期

直接在浏览器中使用免费的 SHA-256哈希生成器,无需安装。

在线试用 SHA-256哈希生成器 →

我搭建的每条部署流水线,最终都需要验证文件校验和、对 Webhook 载荷签名,或为缓存键生成指纹。借助内置的 hashlib 模块,Python SHA-256 哈希可以处理所有这些场景——而且你已经自带了它。 hashlib.sha256() 在 CPython 上封装了 OpenSSL 的实现,速度快且开箱即用符合 FIPS 标准。 如需快速获得一次性哈希而无需编写任何代码, 在线 SHA-256 哈希生成器 可立即给出结果。本文所有示例均以 Python 3.9+ 为目标。

  • hashlib.sha256(data).hexdigest() 是对字节进行哈希的标准方式——属于标准库,底层由 OpenSSL 支持。
  • 字符串必须先编码为字节:hashlib.sha256("text".encode("utf-8"))。
  • 对文件计算校验和时,通过 .update() 分块输入——切勿一次性将大文件读入内存。
  • HMAC-SHA256 需要使用 hmac 模块:hmac.new(key, msg, hashlib.sha256)——SHA-256 本身不支持密钥。

什么是 SHA-256 哈希?

SHA-256(安全哈希算法,256 位)接受任意长度的输入,生成固定的 256 位(32 字节)摘要。相同输入始终产生相同输出,但输入中哪怕只改变一个比特,也会产生完全不同的哈希——这一特性称为雪崩效应。SHA-256 属于 SHA-2 家族,由 NIST 标准化,是 TLS 证书指纹、Git 提交 ID、比特币区块头以及文件完整性验证的核心算法。该算法采用 Merkle-Damgård 结构,经过 64 轮压缩产生 256 位输出。

Before · text
After · text
deployment-v4.2.1
a1f7c3d8e9b2...27ae41e4649b (64 个十六进制字符)

上面的十六进制摘要是标准表示形式——64 个十六进制字符,无论你哈希的是单个字节还是整个磁盘镜像,长度始终相同。

hashlib.sha256() — 标准库方案

hashlib 模块随每个 Python 安装一起提供——无需 pip install。 向 hashlib.sha256() 传入 bytes 参数以创建哈希对象,然后通过 .hexdigest() (十六进制字符串)或 .digest() (原始字节)获取结果。函数名为小写: sha256, 而非 SHA256

Python 3.9+ — 最简 SHA-256 哈希
import hashlib

# 直接对字节字符串哈希
digest = hashlib.sha256(b"deployment-v4.2.1").hexdigest()
print(digest)
# a8f5f167f44f4964e6c998dee827110c3f1de4d0280c68cba98cf70b4b5157db

使用 hashlib.sha256() 最常见的错误是传入 str 而非 bytes。 Python 字符串是 Unicode,而哈希函数处理原始字节。必须在哈希前调用 .encode("utf-8")。 几乎每个人第一次都会在这里踩坑。

Python 3.9+ — 对字符串进行哈希
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)。 这正是分块哈希文件、无需将全部内容加载到内存的方法。

Python 3.9+ — 使用 update() 进行增量哈希
import hashlib

h = hashlib.sha256()
h.update(b"request_id=req_7f3a91bc")
h.update(b"&timestamp=1741614120")
h.update(b"&amount=4999")
print(h.hexdigest())
# 等同于 hashlib.sha256(b"request_id=req_7f3a91bc&timestamp=1741614120&amount=4999").hexdigest()
注意:.digest() 返回原始 32 字节。 .hexdigest() 返回 64 字符十六进制字符串。 当需要将结果输入 HMAC、进行 Base64 编码或写入二进制协议时,使用 .digest()。 用于日志记录、数据库字段和校验和比对时,使用 .hexdigest()

HMAC-SHA256 — 使用 hmac 模块进行带密钥哈希

SHA-256 本身没有密钥的概念——任何人只要拿到相同的输入,就能计算出相同的哈希值。如果需要证明消息来自特定发送方(Webhook 验证、API 请求签名、令牌认证),就需要 HMAC。 hmac 模块是 Python 标准库的一部分,它将密钥嵌入哈希过程,只有持有密钥的人才能生成或验证相同的摘要。

Python 3.9+ — 基础 HMAC-SHA256
import hmac
import hashlib

# Webhook 签名验证
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 字符十六进制 HMAC-SHA256 摘要

验证传入的 HMAC 时,需要使用 hmac.compare_digest() 而非 == 运算符。等号运算符在第一个不匹配的字节处就会短路,攻击者可以通过测量响应时间逐字节猜出正确签名。 compare_digest() 无论不匹配发生在何处,始终以恒定时间运行。

Python 3.9+ — 验证 Webhook 签名
import hmac
import hashlib

def verify_webhook(payload: bytes, received_sig: str, secret: bytes) -> bool:
    """使用恒定时间比较验证 Webhook 签名。"""
    expected = hmac.new(secret, payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, received_sig)

# 模拟 Stripe 风格的 Webhook 验证
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("签名不匹配——拒绝请求")

HMAC-SHA256 请求签名

API 请求签名遵循相同原理:从请求组成部分(方法、路径、时间戳、请求体哈希)构造规范字符串,并用密钥对其签名。AWS Signature V4、Stripe 和 GitHub Webhooks 都使用这一模式的变体。

Python 3.9+ — 使用 HMAC-SHA256 对 API 请求签名
import hmac
import hashlib
import time

def sign_request(method: str, path: str, body: bytes, secret: bytes) -> str:
    """为 API 请求创建 HMAC-SHA256 签名。"""
    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...

Base64 编码的 HMAC-SHA256

某些 API(AWS Signature V4、部分支付网关)期望 HMAC 结果以 Base64 编码字符串而非十六进制表示。区别在于:十六进制使用 64 个字符,而 Base64 对相同的 32 字节摘要只需 44 个字符。

Python 3.9+ — Base64 编码的 HMAC-SHA256
import hmac
import hashlib
import base64

secret = b"webhook_secret_9f3a"
message = b"POST /api/v2/events 1741614120"

# 十六进制输出: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 处理原始字节,因此非字节类型——datetime UUID、dataclass、Pydantic 模型——必须先序列化为字节再进行哈希。没有自动转换;需要自行选择规范表示形式。 为确保跨系统的确定性哈希,始终使用显式编码和稳定的序列化格式(datetime 用 ISO 8601,UUID 用标准字符串形式,dict 用排序键 JSON)。

Python 3.9+ — 对 datetime 和 UUID 进行哈希
import hashlib
import uuid
from datetime import datetime, timezone

# datetime — 使用带显式 UTC 偏移量的 ISO 8601 以确保可移植性
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 哈希:{time_hash[:16]}...")

# UUID — 对规范字符串形式进行哈希(小写,带连字符)
record_id = uuid.uuid4()
uuid_hash = hashlib.sha256(str(record_id).encode("utf-8")).hexdigest()
print(f"UUID 哈希:{uuid_hash[:16]}...")

对于自定义对象,在哈希前将其序列化为规范字节表示。带排序键的 JSON 非常适合类字典对象:

Python 3.9+ — 对自定义对象进行哈希
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 对 dataclass 实例进行哈希,确保确定性。"""
    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))  # 在不同运行和机器上结果稳定
注意:对 JSON 序列化对象进行哈希时,始终对字典键排序(sort_keys=True)。Python 3.7+ 保留了字典插入顺序,但不同序列化路径可能产生不同顺序,导致相同数据产生不同哈希。

SHA-256 文件校验和——验证下载和构建产物

计算文件的 SHA-256 校验和是该算法最常见的用途之一。你随处可见:Go 二进制文件的发布页面、Python wheel 文件、Docker 镜像清单、固件更新。关键在于分块读取文件,而非一次性加载——哈希一个 2 GB 的 ISO 镜像不应该因此消耗 2 GB 内存。

Python 3.9+ — 文件 SHA-256 校验和(分块读取)
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(), 它在内部处理分块读取,并在支持的平台上可能使用零拷贝优化。如果你使用 3.11 或更高版本,推荐使用它代替手动循环。

Python 3.11+ — hashlib.file_digest()
import hashlib

with open("/tmp/release-v4.2.1.tar.gz", "rb") as f:
    digest = hashlib.file_digest(f, "sha256")

print(digest.hexdigest())

对照已知校验和验证下载文件

Python 3.9+ — 校验和验证
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 字符串而非十六进制表示。 HTTP 头部如 Content-Digest Integrity (浏览器的子资源完整性)使用 Base64,JWT 签名也使用 Base64url 编码。 关键是对原始 .digest() 字节进行 Base64 编码,而非对十六进制字符串编码。

Python 3.9+ — Base64 编码的 SHA-256
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 编码(64 个 ASCII 字节 → 88 个 Base64 字符——体积翻倍)
hex_digest = hashlib.sha256(data).hexdigest()
wrong = base64.b64encode(hex_digest.encode()).decode()
print(f"错误长度:{len(wrong)} 个字符")  # 88——API 不期望这个
警告:对十六进制字符串而非原始字节进行 Base64 编码是一个常见错误,会产生两倍于预期长度的输出。API 会拒绝它,而错误信息通常毫无提示。进行 Base64 编码之前,始终调用 .digest(),而非 .hexdigest()

hashlib.sha256() 参考

SHA-256 哈希对象的构造函数及方法:

参数 / 方法
类型
说明
data(位置参数)
bytes
要哈希的初始数据——等同于构造后立即调用 update(data)
.update(data)
bytes
向哈希状态追加字节——可多次调用,用于分块输入
.digest()
→ bytes
返回原始 32 字节二进制摘要——用于 HMAC 输入、二进制协议或 Base64 编码
.hexdigest()
→ str
返回 64 个字符的小写十六进制字符串——用于校验和与验证的标准表示
.copy()
→ hash 对象
返回当前哈希状态的副本——无需从头重新哈希即可分支摘要
hashlib.sha256()
构造函数
创建一个新的 SHA-256 哈希对象,在 CPython 上底层使用 OpenSSL;Python 3.9+ 支持 usedfips=True 限制为 FIPS 批准的算法

带密钥哈希所用 hmac.new() 的参数:

参数
类型
说明
key
bytes
密钥——必须是 bytes,不能是 str
msg
bytes | None
要认证的初始消息——默认为 None,可之后调用 update()
digestmod
str | callable
哈希算法:传入 hashlib.sha256 或字符串 "sha256"

cryptography 库——另一种 SHA-256 API

cryptography 包通过其 hazmat 原语提供了另一种 SHA-256 API。当我只需要哈希时,很少会用到它——hashlib 更简单,且没有外部依赖。但如果你的项目已经依赖 cryptography 用于 TLS、X.509 或对称加密,使用其哈希 API 可以将所有内容集中在一个库下,并获得统一的错误处理。

Python 3.9+ — 使用 cryptography 库进行 SHA-256
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 字符十六进制字符串
# a8f5f167f44f4964e6c998dee827110c3f1de4d0280c68cba98cf70b4b5157db
警告:cryptography 库需要 pip install cryptography。哈希对象是一次性使用的: 第二次调用 .finalize() 会引发 AlreadyFinalized 异常。如需分支哈希状态,请在调用 finalize 前使用 .copy()

对文件和 API 响应体进行哈希

有两种场景频繁出现:对磁盘上的文件进行哈希以验证发布产物,以及对 HTTP 响应体进行哈希以用作缓存键或验证 Webhook。

读取文件 → 计算 SHA-256 → 比较

Python 3.9+ — 带错误处理的配置文件哈希
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 响应体 → 哈希用作缓存键

Python 3.9+ — 对 API 响应体进行哈希
import hashlib
import urllib.request
import json

def fetch_and_hash(url: str) -> tuple[dict, str]:
    """从 API 获取 JSON 并返回数据及其 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')}")

如需快速一次性验证, ToolDeck 的 SHA-256 生成器 完全在浏览器中运行——无需任何代码。

命令行 SHA-256 哈希

有时在处理故障或部署时,只需要在终端快速获取一个哈希值。Python 的 hashlib 模块没有内置的命令行子命令(不像 python3 -m json.tool), 但可以使用单行命令或系统工具。

bash — 从命令行对字符串进行哈希
# 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
bash — 对文件进行哈希
# 对发布压缩包进行哈希
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 与 Shell 之间哈希结果不同的首要原因。

高性能替代方案——hashlib 配合 OpenSSL 与 pycryptodome

在 CPython 上, hashlib.sha256() 已经委托给 OpenSSL 的 C 实现,因此速度很快——在现代硬件上通常可达 500+ MB/s。

如果 SHA-256 哈希出现在你的性能分析报告中——例如在 CI 流水线中为数千个文件计算校验和,或在高吞吐量 API 网关中对每个请求体进行哈希——有两个选择:优化 hashlib 调用模式,或切换到 pycryptodome 以获得同时支持 SHA-3 和 BLAKE2 的统一加密 API:

bash — 安装 pycryptodome
pip install pycryptodome
Python 3.9+ — 使用 pycryptodome 进行 SHA-256
from Crypto.Hash import SHA256

h = SHA256.new()
h.update(b"deployment-v4.2.1")
print(h.hexdigest())
# a8f5f167f44f4964e6c998dee827110c3f1de4d0280c68cba98cf70b4b5157db

对于高吞吐量并行文件哈希,更大的性能提升来自通过增大块大小和多线程来减少 Python 层面的开销:

Python 3.9+ — 使用 hashlib 批量哈希文件
import hashlib
import os
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor

def hash_file(path: Path) -> tuple[str, str]:
    """对单个文件进行哈希,返回(路径,十六进制摘要)。"""
    h = hashlib.sha256()
    with open(path, "rb") as f:
        for chunk in iter(lambda: f.read(65536), b""):  # 64 KB 块
            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 KB 块而非 8 KB 块,Python 到 C 的调用次数减少了 8 倍。由于在 C 层哈希计算期间 GIL 会被释放,多线程在此处效果显著——瓶颈在磁盘 I/O,而非 CPU。

带语法高亮的终端输出

当你需要验证一批文件并希望以表格形式显示每个文件的通过/失败状态,而非让原始十六进制输出滚动而过时, rich 库非常有用。

bash — 安装 rich
pip install rich
Python 3.9+ — 使用 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,
)
注意:rich 输出仅用于终端显示。不要将 ANSI 转义码写入日志文件或 API 响应——使用 console.print(data, highlight=False) 去除高亮, 或通过 Console(file=open(...)) 重定向到文件。

处理大文件

分块 .update() 模式可以以恒定内存用量处理任意大小的文件。对于非常大的文件(多 GB 的磁盘镜像、数据库备份),主要关注点从内存转移到用户反馈——以 500 MB/s 的速度哈希 10 GB 文件仍需 20 秒,这段时间的沉默会让人忐忑。

Python 3.9+ — 带进度报告的大文件哈希
import hashlib
import os

def sha256_with_progress(filepath: str) -> str:
    """哈希大文件,并将进度输出到标准错误。"""
    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 MB 块
            h.update(chunk)
            bytes_read += len(chunk)
            pct = (bytes_read / file_size) * 100
            print(f"\r  正在哈希:{pct:.1f}%({bytes_read >> 20} MB / {file_size >> 20} MB)",
                  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——对每条记录单独哈希

Python 3.9+ — 对 NDJSON 流中的各条记录进行哈希
import hashlib
import json

def hash_ndjson_records(filepath: str) -> dict[str, str]:
    """对 NDJSON 文件中每条 JSON 记录进行哈希,用于去重。"""
    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")
注意:当文件超过 50–100 MB 时,从简单的 hashlib.sha256(data) 单次调用切换到分块 .update() 循环。低于该阈值时,使用 f.read() 读取整个文件也可以——内存用量大致等于文件大小。

常见错误

向 hashlib.sha256() 传入 str 而非 bytes

问题: hashlib.sha256('text') 会引发 TypeError: Unicode-objects must be encoded before hashing。该函数需要 bytes,不接受 str。

解决方法: 先对字符串编码:hashlib.sha256('text'.encode('utf-8'))。对于硬编码值,也可使用 b'' 字面量。

Before · Python
After · Python
import hashlib
digest = hashlib.sha256("deployment-v4.2.1").hexdigest()
# TypeError: Unicode-objects must be encoded before hashing
import hashlib
digest = hashlib.sha256("deployment-v4.2.1".encode("utf-8")).hexdigest()
# 正常运行——返回 64 字符十六进制字符串
使用 == 而非 hmac.compare_digest() 进行签名验证

问题: == 运算符在第一个不匹配字节处短路。攻击者可通过测量响应时间逐字节猜出正确签名。

解决方法: 对所有安全敏感的比较使用 hmac.compare_digest()——无论不匹配发生在何处,它始终以恒定时间运行。

Before · Python
After · Python
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 编码

问题: base64.b64encode(digest.hexdigest().encode()) 产生 88 字符字符串——是预期 44 字符的两倍。期望 Base64 编码 SHA-256 的 API 会拒绝它。

解决方法: Base64 编码前调用 .digest()(原始字节),而非 .hexdigest()(十六进制字符串)。

Before · Python
After · Python
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 GB 的文件仅为了计算哈希就需要 4 GB 内存。

解决方法: 使用循环和 .update() 分块读取。无论文件大小,内存用量保持恒定。

Before · Python
After · Python
import hashlib
# 将整个 4 GB 文件加载到内存
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()
hex / bytes
快(C/OpenSSL)
✓ 通过 update()
否(标准库)
手动 encode()
hmac.new()
hex / bytes
快(C/OpenSSL)
✓ 通过 update()
否(标准库)
手动 encode()
hashlib.file_digest()
hex / bytes
快(零拷贝)
✓(内置)
否(3.11+)
手动 encode()
cryptography hashes.SHA256()
bytes
快(OpenSSL)
✓ 通过 update()
pip install
手动 encode()
subprocess openssl dgst
hex 字符串
✗ / ✓
较慢(fork)
✓(系统级)
系统 openssl
手动 encode()
pyhashcat / 自定义
不定
GPU 加速
pip install
手动 encode()

对于简单哈希——校验和、缓存键、内容指纹——坚持使用 hashlib.sha256()。 一旦需要密钥(Webhook、API 签名、令牌认证),切换到 hmac.new()。 只有当项目已经使用 cryptography 库进行加密或 TLS 时,才考虑使用它——仅为哈希而引入一个 C 扩展依赖是多余的,因为 hashlib 本已底层使用 OpenSSL。

SHA-256 能被解密吗?——哈希与加密的区别

简短回答:不能。SHA-256 是单向函数。该算法被设计为不可逆——无法从 256 位摘要还原原始输入。这不是实现上的限制,而是哈希函数的数学特性。256 位输出空间极为庞大(2256 种可能值),且该函数在 64 轮压缩过程中丢弃了信息。

攻击者可以对弱输入(常用密码、短字符串)尝试暴力破解或字典攻击,但对于任何具有合理熵的输入——API 密钥、随机令牌、文件内容——以当前硬件逆向 SHA-256 在计算上是不可行的。如果需要可逆变换,请使用对称加密:

Python 3.9+ — 加密 vs 哈希
# 哈希——单向,无法还原原始值
import hashlib
digest = hashlib.sha256(b"secret-config-value").hexdigest()
# 无法从 digest 恢复 "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 哈希, 在线工具完全在浏览器中运行。

如何在 Python 中检查字符串是否为有效的 SHA-256 哈希

有效的 SHA-256 十六进制摘要恰好是 64 个十六进制字符(0-9、a-f、A-F)。在处理不可信输入之前进行快速验证,可以避免令人困惑的下游错误。

Python 3.9+ — 验证 SHA-256 格式
import re

def is_sha256_hex(value: str) -> bool:
    """检查字符串是否符合 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 — 无效的十六进制字符
注意:这只验证格式,而非该哈希是否由特定输入计算得出。无法判断一个 64 字符的十六进制字符串是"真实"的 SHA-256 摘要还是随机十六进制——设计上,SHA-256 的输出与随机数据不可区分。

常见问题

如何在 Python 中对字符串进行 SHA-256 哈希?

将字符串编码为字节后调用 hashlib.sha256()。Python 字符串是 Unicode,而哈希函数处理原始字节,因此必须先调用 .encode("utf-8")。.hexdigest() 方法返回常见的 64 字符十六进制字符串。

Python
import hashlib

api_key = "sk_live_9f3a7b2e1d4c"
digest = hashlib.sha256(api_key.encode("utf-8")).hexdigest()
print(digest)
# e3b7c4a1f8d2...64 个十六进制字符

可以将 SHA-256 哈希解密还原为原始文本吗?

不能。SHA-256 是单向函数——它将任意长度的输入映射为固定的 256 位输出,并在计算过程中丢弃了结构信息。数学上不存在逆运算。攻击者可以对弱输入(短密码、常用词)尝试暴力破解或彩虹表查找,但对于具有合理熵的任意输入,逆向 SHA-256 在计算上是不可行的。如果需要可逆变换,请使用加密(AES-GCM、Fernet)而非哈希。

.digest() 和 .hexdigest() 有什么区别?

.digest() 以 bytes 对象形式返回哈希的原始 32 字节。.hexdigest() 将相同数据编码为 64 个字符的小写十六进制字符串。当需要二进制输出时使用 .digest()——例如输入 HMAC、进行 Base64 编码或写入二进制协议。当需要用于日志记录、数据库存储或校验和比对的可读字符串时,使用 .hexdigest()。

Python
import hashlib

h = hashlib.sha256(b"deployment-v4.2.1")
print(len(h.digest()))     # 32(字节)
print(len(h.hexdigest()))  # 64(十六进制字符)

如何在 Python 中计算文件的 SHA-256 校验和?

以二进制模式打开文件,并通过 .update() 分块输入到哈希器中。在 Python 3.11+ 上,使用 hashlib.file_digest() 可获得更简洁的 API。切勿对大文件调用 f.read()——那会将整个文件加载到内存中。

Python
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"))

如何在 Python 中创建 HMAC-SHA256 签名?

使用 hmac 模块,以 hashlib.sha256 作为 digestmod。将密钥和消息以 bytes 形式传入。结果是一个带密钥的哈希,同时证明完整性和真实性——接收方需要使用相同的密钥进行验证。

Python
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 字符十六进制 HMAC

如何验证一个字符串是否为有效的 SHA-256 十六进制摘要?

SHA-256 十六进制摘要恰好是 64 个十六进制字符。使用正则表达式或简单的长度加字符检查即可。正则表达式方式最为直观。

Python
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

相关工具

DV
Dmitri VolkovDevOps Engineer & Python Automation Specialist

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.

MS
Maria Santos技术审阅者

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.