ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、视频、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
## 一. 为什么会有 xss 攻击 因为前端输入的不可信,就会导致恶意代码在浏览器中执行。 1. 通过一个异常的输入,在用户的浏览器中插入恶意的 javascript 脚本。 2. 直接添加一个有恶意脚本的网页,诱导用户打开。 3. 将恶意脚本以话题的方式提交到论坛中,诱使网站管理员打开这个话题,从而获得管理员权限,控制整个网站。 ## 二. xss 攻击的种类 ### 1. 存储型 XSS - 存储在数据库中 * 攻击者将恶意代码提交到目标网站的数据库中。 * 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。 * 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。 * 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。 ![](https://img.kancloud.cn/28/2a/282a0a183ac75ffc299f2e88fa9151e0_1806x1182.png) 这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等 #### 存储型 xss 案例: 1. 在UGC(User-generated Content,用户生产内容,百度贴吧,微博等)业务上,比较容易出现存储型 xss 漏洞。 #### 如何解决: 1. 属性值过滤 2. 重写 参考:腾讯安全应急响应中心:https://security.tencent.com/index.php/blog/msg/53 ### 2. 反射型 XSS ![](https://img.kancloud.cn/ac/5b/ac5be856824db0ee62afa8b3b5a0d269_1656x740.png) 1. 攻击者构造出特殊的 URL,其中包含恶意代码。 2. 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。 3. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。 4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。 反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等 #### 反射型 xss案例: * script 标签出现在 url * jacascript:代码出现在url * jAvascRipt: 代码出现在url 解决:白名单解决法,在白名单内就返回页面,不再一律返回404 某天,公司做需要一个搜索页面,根据 URL 参数决定关键词的内容。代码如下: ``` <input type="text" value="<%= getParameter("keyword") %>"> <button> 搜索 </button> <div> 您搜索的关键字是: <%= getParameter("keyword") %> <div> ``` 如果 黑客构造一下的url, 就可以轻易发起攻击。 ``` http://xxx/search?keyword="><script>alert('XSS');</script> ``` 当浏览器请求 `http://xxx/search?keyword="><script>alert('XSS');</script>` 时,服务端会解析出请求参数`keyword`,得到`"><script>alert('XSS');</script>`,拼接到 HTML 中返回给浏览器。形成了如下的 HTML: ~~~html <input type="text" value=""><script>alert('XSS');</script>"> <button>搜索</button> <div> 您搜索的关键词是:"><script>alert('XSS');</script> </div> ~~~ 问题的原因是,浏览器把用户的输入当成了脚本进行了执行。 解决办法:只要告诉浏览器这段内容是文本就可以解决了 于是有了下面这段代码: ```html <input type="text" value="<%= escapeHTML(getParameter("keyword")) %>" > <button>搜索</button> <div> 您搜索的关键词是: <%= escapeHTML(getParameter("keyword")) %> </div> ``` `escapeHTML()`按照如下规则进行转义: ![](https://img.kancloud.cn/d1/5d/d15d80c3ec3a9366b0fa9874fb745cd4_453x357.png) 转义之后确实可以解决上面的那个利用标签闭合,进行攻击的情况。但是新的问题继续有: 如果 url 是这样的形式:`http://xxx/?redirect_to=javascript:alert('XSS')`, 展示在页面的一个 a 标签上: ~~~html <a href="javascript:alert(&#x27;XSS&#x27;)">跳转...</a> ~~~ 点击 a 标签,还是会弹出 xss,原来不仅仅是特殊字符,连`javascript:`这样的字符串如果出现在特定的位置也会引发 XSS 攻击。 考虑到只要 URL 的开头不是`javascript:`, 是不是就可以解决上面的问题。 代码如下: ~~~java // 禁止 URL 以 "javascript:" 开头 xss = getParameter("redirect_to").startsWith('javascript:'); if (!xss) { <a href="<%= escapeHTML(getParameter("redirect_to"))%>"> 跳转... </a> } else { <a href="/404"> 跳转... </a> } ~~~ 这样就安全了吗? 不是的。 如果 url 的链接是这样的 :`http://xxx/?redirect_to=jAvascRipt:alert('XSS')`,或 `http://xxx/?redirect_to=%20javascript:alert('XSS')` 依旧可以完成 xss 攻击。 那么如何解决呢?可以通过白名单的方法。 代码如下: ~~~java // 根据项目情况进行过滤,禁止掉 "javascript:" 链接、非法 scheme 等 allowSchemes = ["http", "https"]; valid = isValid(getParameter("redirect_to"), allowSchemes); if (valid) { <a href="<%= escapeHTML(getParameter("redirect_to"))%>"> 跳转... </a> } else { <a href="/404"> 跳转... </a> } ~~~ #### 总结: * 攻击者利用用户输入片段,**拼接特殊格式的字符串**,突破原有位置的限制,形成了代码片段。 * 通过 HTML 转义,可以防止部分 XSS 攻击。(不同情况下,需要采用不同的转义规则) * 如果数据是以 json 的形式,内联到 html 中 , 插入 json 的地方不能用 escapeHTML() 转义, 这样会破坏json格式。 * 如果 json 中包含字符串 `</script>` 时, 会闭合前一个 `<script>` 标签,通过增加一个`<script> `标签,就可以完成注入。 * 对于链接跳转,如`<a href="xxx"`或`location.href="xxx"`,要检验其内容,禁止以`javascript:`开头的链接,和其他非法的 scheme。 ### 3. 基于 DOM 的 XSS(其实是一种特殊类型的反射型 xss) ![](https://img.kancloud.cn/5f/57/5f572e9e3a2b2b1a34046761357478c3_1807x934.png) 1. 攻击者构造出特殊的 URL,其中包含恶意代码。 2. 用户打开带有恶意代码的 URL。 3. 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。 4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。 **DOM 型 XSS 跟前两种 XSS 的区别**:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。 #### dom 型 xss 案例 1. 取值写入页面或动态执行 想要在客户端实现接受参数并写入页面或动态执行,就不得不用到JavaScript“三姐妹”,她们分别是:**innerHTML、document.write、eval**。 * 直接从URL的锚参数(即位于#后面的参数)中取值,不经过任何处理直接innerHTML写入页面, 如果攻击者构造这样的 url : `http://xxx.com/xxx.htm#msg=<img /src=x onerror=alter(1)/>` 由于整个攻击不需要像服务端发送任何数据,所以即使业务接入了 web 应用防火墙(WAF),也无法抓到这类 dom xss。 * 从 cookie中取值 直接从cookie中取值写入页面或动态执行,原理基本同从URL中的取参数值写入页面或动态执行,只是换了一个取值来源而已。 但同时我们注意到,还有一种较为特殊的业务场景,取cookie键值,动态拼接要页面引入前端资源的URL。 代码如下: ![](https://img.kancloud.cn/cc/26/cc267b9f6fa8ef243bd618af97ca1393_1832x322.png) 如果window.isp取到的值为`“www.attacker.com/”`,最终拼接出来的静态资源URL路径为:`http://www.attacker.com/victim.com`,因为“.”和“/”都不在转义范围内,导致攻击者可以向页面引入自己站点下的恶意js文件,进而实施DOM-XSS攻击. * 从localStorage、Referer、Window name、SessionStorage中的取参数值写入页面或动态执行 一般情况下 window.name 容易被认为来源可信,但借助 iframe 的 name 属性,攻击者可以将页面的 window.name 设置为攻击代码,构造DOM XSS: ![](https://img.kancloud.cn/82/e6/82e6009722a3b35e16c18f8137a8dca9_1844x50.png) 总结: 1. 在取值写入页面或动态执行的业务场景下,在将各种来源获取到的参数值传入JavaScript“三姐妹”函数(innerHTML、document.write、eval)处理前,对传入数据中的HTML特殊字符进行转义处理能防止大部分DOM-XSS的产生. 2. 根据不同业务的真实情况,还应使用正则表达式,针对传入的数据做更严格的过滤限制,才能保证万无一失。 3. 不到万不得已,不要使用eval函数处理不可控的外部数据。 4. 对于从cookie,还是从localStorage、Referer、Window name、SessionStorage中获取数据,都应使用安全的函数,对传入的数据做过滤后,再传递给相关函数写入页面或执行。 5. 参考/使用filter.js库 #### 2. 前端实现页面跳转 最常用的方式有 location.href 、location.replace() 、 location.assign() 也许提到页面跳转业务场景下的安全问题,你首先会想到限制不严导致任意URL跳转,而DOM XSS与此似乎“八竿子打不着”。但有一种神奇的东西叫“伪协议”,比如:“javascript:”、“vbscript:”、“data:”、“tencent:”、“mobileqqapi:”等,其中“javascript:”、“vbscript:”、“data:”在浏览器下可以执行脚本: 使用这些伪协议执行的JavaScript代码,就相当于在页面内注入了一段恶意代码 * 使用indexOf判断URL参数是否合法, 代码如下 ![](https://img.kancloud.cn/5b/ee/5beedcfc921c1ba26bb2b81d3b8d0f48_1030x582.png) indexOf 是一个从头到尾检索字符串,查找是否有字串 xxx,如果攻击者构造 'javascript:alert(1);//xxx://' 这样的url,即可以成功让 indexOf 返回true,并进入跳转逻辑,完成攻击。又可以让 javascript 代码运行时忽略。(因为 xxx:// 位于JavaScript代码的注释部分) * 正则表达式缺陷 由于 indexOf下藏着的深坑,想出用正则表达式解决这类问题,但是正则表达式也会有一些问题,比如: 跳转页面只能是`qq.com/paipai.com`。 认为这样就可以解决DOM-XSS和URL跳转的问题,但忘了一个神奇的符号“^”,加上和不加上,过滤的效果具有天壤之别。 (因为正则没有严格限制传入的URL开头只能是“http”或“https”,攻击者仍然可以构造“javascript:alert(1);//http://www.qq.com”来绕过看似严格的过滤。) 总结: 1. 限制能够跳转页面的协议:只能是http、https或是其他指可控协议; 2. 严格限制跳转的范围,如果业务只要跳转到指定的几个页面,可以直接从数组中取值判断是否这几个页面。 3. 如果跳转范围稍大,正确使用正则表达式将跳转URL严格限制到可控范围内。 ## XSS 攻击的总结 * 防范存储型和反射型 XSS 是后端的责任 * DOM 型 XSS 攻击不发生在后端,是前端的责任。 * 不同的上下文,调用不同的转义规则 如 HTML 属性、HTML 文字内容、HTML 注释、跳转链接、内联 JavaScript 字符串、内联 CSS 样式表等,所需要的转义规则不一致。 业务 需要选取合适的转义库,并针对不同的上下文调用不同的转义规则。 ## 防止 XSS 攻击 * HttpOnly 防止劫取 Cookie * 不要相信用户的输入 * 对于服务端的返回进行编码或转义