SHA-256校验不一致:先检查末尾换行和Unicode字节

举报
yd_214130845 发表于 2026/10/20 10:10:47 2026/10/20
【摘要】 通过六组UTF-8输入实测SHA-256,说明末尾换行、前导空格和Unicode表示差异为何会改变摘要,并给出浏览器文本、文件校验的最小实现。

排查文本摘要不一致时,先检查输入的原始字节,再怀疑算法。肉眼相似的内容,可能因为换行、前导空格或 Unicode 表示方式不同,得到不同的 SHA-256。下面用六组可复现输入说明。

1. 六组输入的实际结果

本次使用 Node.js 内置 crypto 模块,以 UTF-8 编码计算 SHA-256,并用 Buffer.byteLength 检查字节数。表中摘要只显示前24位,便于对照;完整摘要是64位十六进制字符。

输入(JSON字符串表示) UTF-8字节数 SHA-256前24位
"hello" 5 2cf24dba5fb0a30e26e83b2a
"hello\n" 6 5891b5b522d5df086d0ff0b1
" hello" 6 1c9c535c80b3298829957b9f
"你好" 6 670d9743542cae3ea7ebe36a
"é" 2 4a99557e4033c3539de2eb65
"é" 3 bf12767b0f2a56b2190075ba

其中,"hello" 与 "hello\n" 只差一个末尾换行,字节数由5变为6,摘要也随之改变。前导空格同样属于数据,不能在校验前随意调用 trim()。

两种看起来都是 é 的写法也不同:U+00E9 的 UTF-8 编码是2字节;字母 e 加组合重音 U+0301 是3字节。摘要不会因为它们显示相似就自动将二者视为相同。

2. 在浏览器中复现

以下代码应在支持 Web Crypto 的安全上下文中运行,例如 HTTPS 页面。它保留传入字符串中的空格和换行,再将文本转换为 UTF-8 字节。

async function sha256Text(text) {
  const bytes = new TextEncoder().encode(text);
  const result = await crypto.subtle.digest("SHA-256", bytes);
  const hex = Array.from(new Uint8Array(result), b =>
    b.toString(16).padStart(2, "0")
  ).join("");
  return { bytes: bytes.length, hex };
}

const samples = ["hello", "hello\n", " hello", "你好", "\u00e9", "e\u0301"];
for (const text of samples) {
  console.log(JSON.stringify(text), await sha256Text(text));
}

复制样例时请保留代码中的反斜杠;JavaScript运行后,\n表示一个换行字符,\u00e9和e\u0301分别产生上述两种Unicode表示。

3. 文件校验要直接读取文件字节

文本摘要与文件摘要的输入边界不同。文件可能包含 BOM、CRLF 或其他不能仅凭显示效果确认的字节。校验原文件时,可以直接读取 ArrayBuffer:

async function sha256File(file) {
  const result = await crypto.subtle.digest("SHA-256", await file.arrayBuffer());
  return Array.from(new Uint8Array(result), b =>
    b.toString(16).padStart(2, "0")
  ).join("");
}

不要先把文件显示到文本框、复制出来,再把结果当成原始文件摘要。读取为文本以及编辑、保存等环节,都可能改变换行或编码。文件模式直接处理文件字节,更适合与下载页提供的校验值对照。

4. 排查时固定这三个条件

  1. 输入边界:是否多了末尾换行或首尾空格?日志中的显示文本不一定就是参与计算的字节。
  2. 编码规则:双方是否使用同一种编码?本例统一使用UTF-8。
  3. 规范化规则:仅在协议明确要求时统一Unicode表示、换行或空白;不要为了让摘要一致而临时修改原始数据。

普通SHA-256摘要用于检查字节一致性,不应直接作为密码存储方案。若业务需要对消息做带密钥的完整性校验,应按具体协议选用HMAC等机制。

实现依据:WHATWG Encoding Standard中的TextEncoder接口,以及W3C Web Cryptography规范中的SubtleCrypto.digest方法。本文代码与表格由上述六组输入实际运行获得,正文为原创整理。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。