Table of Contents
跨场脚本( XSS) 和 JavaScript 防御的介绍
跨站脚本(XSS)是最普遍的网络安全漏洞之一,在OWASP Top 10. 中始终排位. XSS攻击允许攻击者将恶意客户端脚本注入其他用户浏览的网页. 这些脚本可以偷取会话令牌,将用户重定向到钓鱼网站,去脸页,或者安装恶意软件. 虽然服务器侧的消毒至关重要,但JavaScript在客户端扮演着关键角色,以检测和防止这些攻击. 本文提供了一个全面的,可制作的指南,可以使用JavaScript来保护您的应用程序免受XSS的伤害.
理解SS的三种类型
在潜入预防之前,必须了解XSS的三大类:存储,反射,以及基于DOM的。 每种都要求有稍有不同的检测和预防方法。
存储 XSS
存储(peristent) XSS 发生在恶意输入永久存储在服务器上(例如,在数据库,论坛帖或评论中),后来提供给用户,但没有适当的消毒. 攻击有效载荷执行在浏览器中查看存储内容的任何人.
反映的预算外
反射 XSS 发生在恶意脚本从网络服务器上反射, 通常是通过 URL 参数或格式提交。 攻击者会欺骗受害者点击一个已设计的链接, 而注入的代码会立即执行。 与存储 XSS 不同, 有效载荷不会持续 。
基于DOM的用户支持系统
基于 DOM 的 XSS 是一个纯粹的客户端弱点。 攻击有效载荷会改变受害者浏览器中的 DOM 环境。 恶意代码从来不会触碰服务器; 它来自客户端的 JavaScript , 它不安全地处理用户输入( 例如, 读自 [[FLT: 0] , [[FLT: 1] 或 [[FLT: 2] ) 。
用 JavaScript 探测 XSS 攻击
检测是在发生损坏前识别可疑活动. JavaScript 可以监视用户输入,跟踪DOM突变,并在入口处验证数据. 虽然客户端检测无法捕捉所有攻击(特别是攻击者手工业直接请求服务器),但它提供了宝贵的第一线防御.
输入验证和定级
处理前总是在客户端验证和消毒用户输入。 使用 而不是 来阻止脚本执行。 以下函数从字符串中剥离危险字符 :
function sanitizeInput(input) {
const div = document.createElement('div');
div.textContent = input;
return div.innerHTML;
}
这样做是因为它的设置没有解释HTML标记;它把一切当作纯文本处理。结果包含HTML任何特殊字符的逃逸版本(例如,,).
监测可疑因素的DOM变异
攻击者经常向 DOM 注入 标记或事件处理器(,]]。使用 API,您可以查看意外元素插入。一个基本的例子:
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === 1) { // element node
if (node.tagName === 'SCRIPT') {
console.warn('Potential XSS: a script element was injected via DOM.');
node.remove(); // or log and analyze
}
// Check for dangerous attributes
if (node.hasAttribute('onerror') || node.hasAttribute('onload')) {
console.warn('Suspicious event handler attribute detected.');
}
}
});
});
});
observer.observe(document.body, { childList: true, subtree: true });
Caution:[]通过]]屏蔽脚本,可以被聪明的攻击者绕过,并可能破坏合法功能. 将此作为监测工具而不是初级预防机制.
验证 URL 和 Hash 参数
将基于 DOM 的 XSS 改为 安全使用 [[FLT: 17]] 的 URL 组件,并避免直接插入到 HTML 中。 检测试图通过可执行代码的尝试 :
const params = new URLSearchParams(window.location.search);
const userParam = params.get('name');
if (userParam && /[<>"'\/]/.test(userParam)) {
console.warn('Potential XSS in parameter: ' + userParam);
// Do not use this value in the DOM without encoding
}
使用 JavaScript 防止 XSS 攻击
预防需要多层次的方法. JavaScript单靠一个应用无法完全保证安全,但如果结合适当的后端消毒和的连锁安全政策,则会大幅降低风险.
在插入 DOM 之前编码所有用户控制的数据
金规则: 永远不要直接将不信任的数据插入到DOM中. 使用安全的DOM方法而不是内置HTML.
使用或]
const userInput = getUserInput();
const safeText = document.createTextNode(userInput);
document.getElementById('output').appendChild(safeText);
当您必须使用 [[FLT: 22] ] 时, 使用库进行 Sanitize
如果您绝对需要使 HTML(例如从丰富的文本编辑器) , 则依赖像 [[FLT: 0]] DOMPurify [[[FLT: 1]] 这样的可信任的消毒库. DOMPurify 是广泛使用的,经过战斗测试的库, 在保存安全 HTML的同时删除恶意代码.
// Example with DOMPurify (install via npm or CDN)
const dirty = '<img src=x onerror="alert(1)">';
const clean = DOMPurify.sanitize(dirty);
document.getElementById('content').innerHTML = clean;
DOMPurify 的作品,通过解析输入,剥离危险的标记和属性,并返回只允许的元素. View DOMPurify on GitHub .
避免危险的 Java 脚本函数
一些JavaScript方法和属性因启用 XSS而臭名昭著. 避免或严格控制:
- ]——使用]或适当消毒.
- [],]——同一规则。
- ]——从不使用用户输入.
- ]——如果任何输入被调和,可以被利用.
- ] /[]]]字符串代码[——避免;使用函数引用代替.
- ] 构造器[——类似于]]]。
通过 JavaScript 执行内容安全政策( CSP) ? 不推荐
CSP是一种限制脚本运行的浏览器机制,它通常通过HTTP头设定,但您也可以通过动态创建元素来设置它,或者通过JavaScript. 然而,在JavaScript中设置CSP的安全性较差,因为已经拥有某种控制的攻击者可以禁用它。总是倾向于HTTP头。如果您必须使用JavaScript强制使用CSP(例如开发期间),那么在页面加载时要很早:
const meta = document.createElement('meta');
meta.httpEquiv = 'Content-Security-Policy';
meta.content = "default-src 'self'; script-src 'self' 'unsafe-inline'"; // Be very careful with 'unsafe-inline'
document.head.appendChild(meta);
对于生产,在您的网络服务器或反向代理中配置 CSP. MDN CSP文档[ 提供了全面的指导.
补充安全措施
除了JavaScript特有的战术外,一个完整的XSS预防战略包括这些关键措施:
- 总是在服务器侧验证. 客户端验证可以绕过,永远不要信任客户端数据.
- 使用适当的HTTP响应头。 ,,特别是]。
- 每次提供用户数据时输出编码. 上下文事项: HTML 实体的编码, URL 编码, JavaScript 字符串编码等.
- 保持依赖性更新. 脆弱 JavaScript库(如jQuery的旧版本)是常见的XSS向量. 使用 npm 审计或类似的工具.
- 使用带有内置 XSS 保护的框架。 React, Angular,和 Vue 默认自动逃出输出。但是,要谨慎,使用 或 ]。
- 执行严格的CSP. 避免]和] 可能的话,使用非色或散列来进行内置脚本.
真实世界实例: 安全注释渲染
考虑一个博客评论系统, 用户将显示给其他人的信息提交到其中。 攻击者可能会尝试插入 [[FLT: 44]]。 以下是一个与后端整合的 JavaScript 方法 :
- 前线提交:[] 在发送到服务器之前使用]的卫生处理(但服务器仍必须进行消毒).
- Server返回数据: 后端应该将注释文本HTML-encode.
- 客户端渲染:[]使用]或安全模板引擎。从不使用 原始用户数据。
function renderComment(comment) {
const item = document.createElement('div');
item.className = 'comment';
const body = document.createElement('p');
body.textContent = comment.body; // escaped by browser
item.appendChild(body);
document.getElementById('comments').appendChild(item);
}
测试您的防御
实施预防后,使用自动扫描仪和人工有效载荷测试您的应用。
- ]]
- []]
- ]]
- ]]
- ]]
使用浏览器开发工具检查 DOM 并确保有效载荷被逃脱。 另外,通过检查控制台的违规报告来测试 CSP 执行 。
结论
跨站点脚本仍然是严重的威胁,但JavaScript提供了强大的检测和预防工具。 通过验证输入、监测DOM变化、逃避输出以及整合像DOMPurify这样的强大的库,您可以大大加强客户端的安全。 记住客户端的措施不是银弹;它们补充了深入防御的战略,包括服务器端的消毒、CSP头和定期的安全审计。 保持警惕、经常测试和保持库的最新状态。
欲进一步阅读,请查阅 OWASP XSS页和 OWASP XSS 防止加热表。