前端安全系列(一):如何防止XSS攻击?
字数 2922 2025-08-18 11:37:41
XSS攻击全面防护指南
一、XSS攻击概述
XSS(Cross-Site Scripting,跨站脚本攻击)是一种代码注入攻击,攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。XSS的本质是:恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。
XSS攻击的危害
- 窃取用户敏感信息(如Cookie、SessionID)
- 冒充用户执行操作
- 传播恶意内容
- 发起"XSS蠕虫"攻击
二、XSS攻击分类
1. 存储型XSS
- 存储区:后端数据库
- 插入点:HTML
- 攻击流程:
- 攻击者提交恶意代码到数据库
- 用户访问页面时从数据库取出恶意代码
- 浏览器执行恶意代码
常见场景:论坛发帖、商品评论、用户私信等用户可提交持久化数据的场景。
2. 反射型XSS
- 存储区:URL
- 插入点:HTML
- 攻击流程:
- 攻击者构造含恶意代码的URL
- 用户点击URL,服务端取出恶意代码返回
- 浏览器执行恶意代码
常见场景:网站搜索、跳转等通过URL传递参数的功能。
3. DOM型XSS
- 存储区:后端数据库/前端存储/URL
- 插入点:前端JavaScript
- 攻击流程:
- 攻击者构造含恶意代码的URL
- 用户点击URL
- 前端JS取出URL中的恶意代码并执行
与前两种的区别:DOM型XSS完全在前端完成,不经过服务端处理。
三、XSS攻击的注入方式
- 在HTML中内嵌的文本中,以
<script>标签形式注入 - 在内联JavaScript中,突破字符串、变量等限制
- 在标签属性中,通过引号突破属性值限制
- 在
href、src等属性中,包含javascript:等可执行代码 - 在
onload、onerror、onclick等事件中注入代码 - 在
style属性和标签中,包含类似background-image:url("javascript:...");的代码 - 在
style属性和标签中,包含类似expression(...)的CSS表达式代码
四、XSS防御策略
1. 输入过滤的局限性
- 前端过滤可被绕过
- 后端过滤可能导致乱码问题
- 不同上下文需要不同的转义规则
建议:仅对明确类型的输入(数字、URL、电话等)进行过滤
2. 存储型和反射型XSS防御
(1) 纯前端渲染
- 浏览器先加载静态HTML
- 通过Ajax加载业务数据
- 明确告诉浏览器设置内容的类型(
.innerText、.setAttribute等)
优点:有效隔离代码和数据
注意:仍需防范DOM型XSS
(2) HTML转义
使用完善的转义库,针对不同上下文采用不同转义规则:
| 上下文 | 转义函数示例 | 转义内容 |
|---|---|---|
| HTML标签文字内容 | Encode.forHtml() |
&, <, >, ", ', / |
| HTML属性值 | Encode.forHtml() |
同上 |
| CSS属性值 | Encode.forCssString() |
防止突破CSS字符串 |
| CSS URL | Encode.forCssUrl() |
防止注入恶意URL |
| JavaScript字符串 | Encode.forJavaScript() |
防止突破JS字符串 |
| 内联JSON | Encoder.forJavaScript(data.to_json) |
转义U+2028、U+2029、<等 |
| URL参数 | Encode.forUriComponent() |
URL编码 |
| URL路径 | Encode.forUriComponent() |
URL编码 |
3. DOM型XSS防御
- 避免使用
.innerHTML、.outerHTML、document.write() - 使用
.textContent、.setAttribute()等安全API - 避免将不可信数据拼接到以下场景:
- 内联事件监听器(
onclick等) <a>标签的href属性eval()、setTimeout()、setInterval()等可执行字符串的API
- 内联事件监听器(
4. 其他防御措施
(1) Content Security Policy (CSP)
- 禁止加载外域代码
- 禁止外域提交
- 禁止内联脚本执行
- 禁止未授权的脚本执行
(2) 输入内容长度控制
对不受信任的输入限制合理长度,增加攻击难度
(3) 安全措施
- HTTP-only Cookie:防止JS读取敏感Cookie
- 验证码:防止脚本冒充用户操作
五、XSS检测方法
1. 手动检测
使用通用XSS攻击字符串测试,例如:
jaVasCript:oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e
2. 自动扫描工具
- Arachni
- Mozilla HTTP Observatory
- w3af
六、最佳实践原则
-
利用模板引擎:
- 开启自带的HTML转义功能
- ejs使用
<%= data %>而非<%- data %> - doT.js使用
{{! data }}而非{{= data }} - FreeMarker确保版本>2.3.24
-
避免内联事件:
- 使用
.addEventListener()而非onclick="go('{{action}}')"
- 使用
-
避免拼接HTML:
- 使用
createElement、setAttribute - 或使用Vue/React等框架
- 使用
-
保持警惕:
- 特别注意DOM属性、链接等位置
-
增加攻击难度:
- 使用CSP、输入长度限制等
-
主动检测:
- 定期使用XSS字符串和扫描工具检测
七、常见误区
-
误区一:XSS防范只是后端的责任
- 事实:存储型和反射型是后端责任,DOM型是前端责任
-
误区二:所有数据都通过同一个过滤函数转义
- 事实:不同上下文需要不同转义规则
-
误区三:转义应该在提交用户输入时进行
- 事实:转义应在输出HTML时进行
八、真实案例
1. QQ邮箱反射型XSS
- 漏洞点:
uin、domain参数未转义直接输出 - 攻击URL:包含恶意脚本的URL参数
- 结果:窃取用户Cookie
2. 新浪微博名人堂反射型XSS
- 漏洞点:URL内容未过滤直接输出
- 攻击方式:XSS蠕虫,诱导更多人点击
- 结果:利用受害者身份发布恶意内容
九、进阶防护:Automatic Context-Aware Escaping
概念:模板引擎自动分析插入点上下文,应用合适的转义规则。
支持引擎:
- Go html/template
- Google Closure Templates
示例:
<html>
<head>
<meta charset="UTF-8">
<title>{{.title}}</title>
</head>
<body>
<a href="{{.url}content}}</a>
</body>
</html>
引擎自动转换为:
<html>
<head>
<meta charset="UTF-8">
<title>{{.title | htmlescaper}}</title>
</head>
<body>
<a href="{{.url | urlescaper | attrescaper}content | htmlescaper}}</a>
</body>
</html>
十、总结
XSS防御是系统工程,需要前后端协作:
- 后端:处理存储型和反射型XSS
- 前端:处理DOM型XSS
- 共同:实施CSP等安全策略
关键点:
- 理解不同上下文的不同转义需求
- 使用成熟的转义库而非自己实现
- 避免危险的API(如
innerHTML) - 定期检测和修复漏洞