下载完文件怕被改写?写了个前端哈希校验器,上传即算摘要
从网盘、镜像站下完安装包,第一反应该是校验哈希,确认没被中间人篡改。可在线校验网站要把文件传上去,等于把隐私文件也交出去了。我写了个纯前端哈希校验器:选本地文件,浏览器当场算出摘要,文件不出本机。用的是 Web Crypto 的 SHA-256——顺带也讲清楚为什么浏览器原生算不了 MD5。
一、为什么用 SHA-256 而不是 MD5
MD5 因为碰撞漏洞早已不推荐用于安全校验,更现实的是:浏览器原生没有提供 MD5。Web Crypto API(crypto.subtle)只给了 SHA-1/256/384/512,没有 MD5。想在浏览器算 MD5 只能引入第三方库(如 spark-md5),把文件读进内存再自己实现算法。
所以我这版直接用原生 SHA-256,零依赖、速度快,校验下载完整性完全够用。下载站给的如果是 MD5,那就用库补一个,思路一致。
二、读文件 + 算哈希
用 FileReader 把文件读成 ArrayBuffer,交给 crypto.subtle.digest:
async function hashFile(file) {
const buf = await file.arrayBuffer(); // 读成二进制
const digest = await crypto.subtle.digest('SHA-256', buf);
return [...new Uint8Array(digest)]
.map(b => b.toString(16).padStart(2, '0'))
.join(''); // 转十六进制串
}
file.arrayBuffer() 比老式的 FileReader.readAsArrayBuffer 写法更短。算出来的 digest 是二进制,用展开运算符转成字节数组,每个字节 toString(16) 补零拼成十六进制串——这才是人能读的哈希值。
三、大文件也要稳
几百 MB 的文件一次性读进内存没问题,但超大文件该用流式 crypto.subtle.digest 不支持分块,真要分块得自己攒 buffer。日常场景一次性读足够,记得在算的期间给个"计算中"提示,不然用户以为卡了:
fileInput.addEventListener('change', async () => {
const file = fileInput.files[0];
tip.textContent = '计算中…';
const hex = await hashFile(file);
tip.textContent = '';
result.value = hex;
});
哈希算法对比
| 算法 | 浏览器原生 | 输出长度 | 现状 |
|---|---|---|---|
| MD5 | 否(需库) | 128 位 | 已不推荐用于安全 |
| SHA-1 | 是 | 160 位 | 逐步淘汰 |
| SHA-256 | 是 | 256 位 | 当前主流 |
下载站给 SHA-256 就直接比;给 MD5 只能靠库,且别拿 MD5 当安全证明。
几个容易踩的点
- 大小写比对:哈希串有大小写之分,比对前统一
.toLowerCase(),不然AB和ab被认为不等。 - ArrayBuffer 只读一次:
file.arrayBuffer()可以反复调,但老式FileReader的result读一次就废,混用容易踩。 - 老浏览器兼容:
crypto.subtle要求 HTTPS 或 localhost,用file://直接打开可能拿不到,本地起个静态服务再开。 - 别拿哈希当加密:哈希是单向的,只能"比对"不能"还原",校验完整性可以,藏秘密不行。
文章总结
前端哈希校验器的核心是 FileReader/arrayBuffer 读文件加 crypto.subtle.digest('SHA-256', buf),再把二进制转十六进制串。选 SHA-256 是因为它浏览器原生支持、且比 MD5 更安全——MD5 既不安全也不被原生提供,硬要只能引库。文件全程在本地内存算,不上传不变服务器,校验下载包完整性正好。比对时统一大小写,老环境注意 crypto.subtle 要 HTTPS 才生效。
- 点赞
- 收藏
- 关注作者
评论(0)