背景
我网站执行的 JS 当然是我自己写的 JS 呀,难不成还有安全问题??但想想,很多时候网站的 JS 是从第三方的 CDN 服务加载下来的,如果CDN服务器受到了攻击,JS 文件被篡改,就会带来安全风险,如何保证我们网站运行的这些脚本文件是未被修改的呢?
还有一种情况就是 html 中可能有一些 inline script,这些 JS 可能是在服务端渲染 html 模版的时候注入的,如果网络传输过程中被抓包篡改了怎么办?
伪代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
</head>
<body>
<!-- inject-placeholader -->
<div id="root"></div>
</body>
</html>
router.get("/", async (ctx) => {
const htmlTpl = await getHtmlTpl();
const script = `window.__SOME_DATA__ = ${JSON.stringify(__SOME_DATA__)};`;
ctx.type = "html";
ctx.body = htmlTpl.replace("<!-- inject-placeholader -->", `<script>${script}</script>`);
});
先来看看传统软件是怎么保证用户获取到的内容是未篡改的。比如 linuxkit/linuxkit,他的发布页面会附上 Checksums
:
这些 hash 值是根据文件内容,通过固定的算法得到的,比如上面的 SHA256。这样获取到文件的用户用同样的算法做一次计算,如果得到的 hash 值和作者标明的一致,就可以验证软件没有被别人篡改过的。
回到前端,前端有没有类似的安全方案呢?答案是肯定的。
1. Subresource Integrity(SRI)
我们给 <script>
或 <link>
标签添加 integrity
属性,属性值是根据文件内容做 hash 算法得到的一个字符串。形如:
<link href="//somecdn.com/foo.css" rel="stylesheet" integrity="sha256-t7Z7PgokIxRooJ8azMRTqZZIdgaQX6ViGg3pn3pxZZs= sha384-KJi9xVfT8JzG/tFq6Dpgw6URtNE3WK83VaQOWpfHsVAN6Az5+AjliGZuSiiWd4ah" crossorigin="anonymous">
<script src="//somecdn.com/bar.js" integrity="sha256-XXVAhVe8STxZbyQhPOwZpZmx3X9iHnnrBPHUN/4vooc= sha384-438vOegRAvOckkDAIIIL8+k0JhRCRfY7Q2QXLjgFOHQbhyFK/YwGIDJBxYCdaHjA" crossorigin="anonymous"></script>
其中的 sha256
和 sha384
是 hash 算法,而 sha256-
或 sha384-
后面的部分是 hash code, 对于跨域的脚本请求,脚本服务器需要设置 CORS 响应头,允许跨域站点访问他的内容 Access-Control-Allow-Origin: *
, <script>
或 <link>
标签上也需要添加 crossorigin 属性,anonymous 代表请求不携带 cookie。
对于设置了 SRI 的脚本或样式,浏览器会对请求下载的文件内容做同样的 hash 算法,如果 hash code 不匹配,就会拒绝执行。
webpack 的项目可以通过 webpack-subresource-integrity 在打包时自动添加 Integrity 和 anonymous,配置如图:
2. Content-Security-Policy (CSP)
对于一些行内脚本,我们可以通过设置 CSP 的 方式来校验。比如:
router.get("/", async (ctx) => {
const htmlTpl = await getHtmlTpl();
const script = `window.__SOME_DATA__ = ${JSON.stringify(__SOME_DATA__)};`;
const hash = require("crypto").createHash("sha256").update(script).digest("base64");
ctx.set("Content-Security-Policy", `script-src 'self' 'sha256-${hash}'`);
ctx.type = "html";
ctx.body = htmlTpl.replace("<!-- inject-placeholader -->", `<script>${script}</script>`);
});
上面的 CSP 设置保证了只有 hash 值和我们给定的 hash 值一致 inline-script 才会被执行,对于不符合的 inline-script,会抛出如下错误:
不过话说回来,既然算法是固定的,交付 hash code 的过程也是通过网络传输到前端的(html 内容,response header),如果有人能在网络传输的过程中修改响应,那么他把恶意代码做一次同样的算法,然后把 hash code 也改掉,还是无法避免不安全脚本的出现。这就要求我们要保证网络传输的安全。一方面我们需要给网站配置 https 避免明文传输,另一方面也要告知用户不要在访问网站的时候使用不可信代理。
补充
顺便说一下,保证内容可靠性的另一种常见做法。对接过微信公众号消息的同学可能有印象,微信的服务器会给我们的服务器推送消息,我们如何保证消息是微信的服务器发送过来的,而不是其他人伪造的呢?做法就是微信和我们自己的服务器都持有同一个 Token。微信发过来的消息中有一个签名 signature,这个签名就是由请求的内容和这个 Token 共同参与生成的。只要保证微信侧和我们自己的服务持有同一个Token,对同样的内容,做同样算法的签名就可以,如果最终得到的签名一致就说明请求确实是微信发给我们的。可以参考我之前写过的 Node.js 对接微信消息通知的 示例。
这种做法不同于上面的一点是双方都保留了一个不为第三方所知的 Token,只要 Token 没泄露,即使别人知道你们的签名算法是啥,也无法伪造出一个签名。这种方式并不适用于前端,因为前端无法不通过网络把一个 “Token” 预先埋在所有用户的系统里。
参考链接
developer.mozilla.org/en-US/docs/…
developer.mozilla.org/zh-CN/docs/…
github.com/waysact/web…
webpack.js.org/configurati…
developer.mozilla.org/en-US/docs/…
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!