KernelRanch
返回文章列表

静态资源返回 200 却加载失败:排查 SRI 摘要不匹配

2026-11-13 #web#security#cdn#nodejs

脚本地址返回 HTTP 200,加载仍可能被完整性校验拒绝。SRI 将页面里声明的摘要与实际取得的资源字节比较。构建工具或 CDN 即便只改写一个换行符,也会改变摘要。排查时应对照部署清单与客户端实际取得的字节。

1. 本机复现

保存为 integrity_lab.mjs,运行 node integrity_lab.mjs。同一个本地服务返回原始样本和多一个换行的样本,用 Fetch 的 integrity 参数校验。

import assert from 'node:assert/strict';
import { createHash } from 'node:crypto';
import { once } from 'node:events';
import { createServer } from 'node:http';

const original = 'console.log("ok");\n';
const changed = original + '\n';
const sri = 'sha384-' + createHash('sha384').update(original).digest('base64');
const server = createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/javascript; charset=utf-8' });
  res.end(req.url === '/original.js' ? original : changed);
});
server.listen(0, '127.0.0.1');
await once(server, 'listening');
const base = 'http://127.0.0.1:' + server.address().port;

try {
  const originalResponse = await fetch(base + '/original.js', { integrity: sri });
  assert.equal(await originalResponse.text(), original);
  console.log('matching SHA-384:', originalResponse.status);

  const changedResponse = await fetch(base + '/changed.js');
  assert.equal(changedResponse.status, 200);
  assert.equal(await changedResponse.text(), changed);
  console.log('changed resource without integrity:', changedResponse.status);

  await assert.rejects(fetch(base + '/changed.js', { integrity: sri }), TypeError);
  console.log('changed resource with integrity: rejected');
} finally {
  server.close();
  await once(server, 'close');
}

本地实测输出如下。

matching SHA-384: 200
changed resource without integrity: 200
changed resource with integrity: rejected

2. 把摘要绑定到发布产物

  1. 在压缩和打包步骤完成后,对最终产物生成 sha384- 加 Base64 摘要。HTTP 传输采用内容压缩时,核对的是 Fetch 解码后的资源内容。
  2. 给产物使用版本化 URL,把该版本的 URL 和摘要一起写入 HTML。发布和回滚时同时核对这两个值,并检查 CDN 是否又做了换行转换、二次压缩混淆或内容注入等字节改写。
  3. 跨源脚本还要满足 CORS。部署时可以用 crossorigin="anonymous" 发起跨源请求,并确认资源服务器返回适当的允许来源响应头。HTTP 200 只能说明请求到了可响应的地址,浏览器还会执行跨域和完整性校验。

小结

对实际发布的资源内容生成摘要,并随资源版本一起更新。先核对客户端拿到的字节,再排查跨源加载条件。

数据来源与检索时间

检索时间 2026-11-13。

来源URL对应内容
W3C Subresource Integrity 工作草案https://www.w3.org/TR/2026/WD-sri-2-20260320/摘要格式、验证过程和跨源约束
WHATWG Fetchhttps://fetch.spec.whatwg.org/#concept-request-integrity-metadata请求完整性元数据

核验说明

  • Windows、Node.js v24.19.0 在回环接口验证 SHA-384 匹配和多一个换行导致的拒绝。浏览器脚本元素、跨源 CORS 与 CDN 部署依据规范说明。