防御修复
XSS 防御与修复
Content Security Policy
Github 使用 Electron 构建编辑器 Atom,其使用了 CSP 来限制潜在的 XSS 代码执行:
// index.html
<!DOCTYPE html>
<html>
<head>
<meta
http-equiv="Content-Security-Policy"
content="default-src * atom://*; img-src blob: data: * atom://*; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src blob: data: mediastream: * atom://*;"
/>
<script src="index.js"></script>
</head>
<body tabindex="-1"></body>
</html>
The script-src ‘self’ ‘unsafe-eval’, means that JavaScript from the same origin as well as code created using an eval like construct will by be executed. However, any inline JavaScript is forbidden.
In a nutshell, the JavaScript from “index.js” would be executed in the following sample, the alert(1) however not, since it is inline JavaScript:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src * atom://*; img-src blob: data: * atom://*; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src blob: data: mediastream: * atom://*;">
</head>
<!-- Following line will be executed since it is JS embedded from the same origin -->
<script src="index.js"></script>
<!-- Following line will not be executed since it is inline JavaScript -->
<script>alert(1)</script>
</html>
React
笔者一直是坚定地 React 技术栈的使用者,因此也会关注 React 应用安全相关的话题。笔者在我自己的脚手架的第三层级也使用了大量的服务端渲染/同构直出的技术,而本文即是阐述该方法可能存在的某个 XSS 漏洞。服务端渲染即允许我们在服务端进行 HTML 渲染,并且在服务端请求部分应用数据追加到页面上然后随着页面一起返回给用户,从而减少用户的首屏等待时间,并且对于搜索引擎有更友好的优化。
不过如果有安全背景的朋友肯定已经能够察觉到问题了,直接将数据不经过滤地放到页面上势必会带来潜在的安全问题,譬如我们最常用的同构页面的代码:
export default (html, initialState = {}, scripts = [], styles = []) => {
return `
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
${styleMapper(styles)}
</head>
<body>
<div id="root">${html}</div>
</body>
${scriptMapper(scripts)}
<script>
window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};
</script>
</html>
`;
};
我们直接使用JSON.stringfy
将 JavaScript 对象转化为了 JSON 字符串,然后以全局变量的方式插入到了页面中。不过如果你要序列化的对象是如下这样呢:
{
"user": {
"username": "NodeSecurity",
"bio": "as</script><script>alert('You have an XSS vulnerability!')</script>"
}
}
你就会很开心的看到你得到了某个弹窗。关于 XSS 的知识点笔者不在这里赘述,虽然我们的后台开发人员肯定也在他们的接口层与数据库层完成了敏感字段过滤,不过千里之堤毁于蚁穴,我们不能放过任何一处有可能产生问题的地方。
对于 XSS 的防御也并不是新鲜的话题,著名的Open Web Application Security Project项目就为我们提供了很多关于防止 XSS 攻击的建议,概括而言,我们需要在应用中做到如下几点:
-
所有的用户输入都需要经过 HTML 实体编码,这里 React 已经帮我们做了很多,它会在运行时动态创建 DOM 节点然后填入文本内容(你也可以强制设置 HTML 内容,不过这样比较危险)
-
当你打算序列化某些状态并且传给客户端的时候,你同样需要进行 HTML 实体编码
Yahoo 的工程师已经提供了一个 Serialize JavaScript 模块帮我们轻松地进行 JSON 转码与过滤,我们可以直接使用 npm install --save serialize-javascript
导入该模块,然后使用serialize
方法替代内置的JSON.stringify
方法: