概述
跨站点脚本 (XSS) 攻击是一种注入,其中恶意脚本被注入到其他良性和受信任的网站中。当攻击者使用 Web 应用程序将恶意代码(通常以浏览器端脚本的形式)发送给不同的最终用户时,就会发生 XSS 攻击。允许这些攻击成功的缺陷非常普遍,并且发生在 Web 应用程序在其生成的输出中使用来自用户的输入而不对其进行验证或编码的任何地方。
攻击者可以使用 XSS 向毫无戒心的用户发送恶意脚本。最终用户的浏览器无法知道该脚本不应被信任,并将执行该脚本。因为它认为脚本来自受信任的来源,所以恶意脚本可以访问浏览器保留并与该站点一起使用的任何 cookie、会话令牌或其他敏感信息。这些脚本甚至可以重写 HTML 页面的内容。
使用 SSL (https) 的网站不会比未加密的网站更受保护。Web 应用程序的工作方式与以前相同,只是攻击发生在加密连接中。人们通常认为,因为他们看到浏览器上的锁就意味着一切都是安全的。事实并非如此。
一个经典场景
当 Web 应用程序从用户那里收集恶意数据时,就会发生跨站脚本(也称为 XSS)。数据通常以超链接的形式收集,其中包含恶意内容。用户很可能会从另一个网站、即时消息或只是阅读网络板或电子邮件消息中单击此链接。通常,攻击者会以 HEX(或其他编码方法)对指向站点的链接的恶意部分进行编码,这样当用户点击请求时,请求就不会那么可疑了。Web 应用程序收集数据后,它会为用户创建一个输出页面,其中包含最初发送给它的恶意数据,但以某种方式使其显示为来自网站的有效内容。许多流行的留言簿和论坛程序允许用户提交嵌入了 html 和 javascript 的帖子。例如,如果我以“john”的身份登录并阅读了“joe”发送的包含恶意 javascript 的消息,那么“joe”可能会通过阅读他的公告栏帖子来劫持我的会话。
攻击类型
存储型 XSS 攻击
存储攻击是将注入的脚本永久存储在目标服务器上的攻击,例如数据库、消息论坛、访问者日志、评论字段等。然后受害者在请求存储时从服务器检索恶意脚本信息。存储型 XSS 有时也称为 Persistent 或 Type-I XSS。
// 某个评论页,能查看用户评论。
// 攻击者将恶意代码当做评论提交,服务器没对数据进行转义等处理
// 评论输入:
<textarea>
<img src="empty.png" onerror ="alert('xss')">
</textarea>
// 则攻击者提供的脚本将在所有访问该评论页的用户浏览器执行
复制代码
反射型 XSS 攻击
反射式攻击是指注入的脚本从 Web 服务器反射出来的攻击,例如在错误消息、搜索结果或任何其他响应中,其中包括作为请求的一部分发送到服务器的部分或全部输入。反射攻击通过其他途径传递给受害者,例如在电子邮件消息中或在其他网站上。当用户被诱骗点击恶意链接、提交特制表单,甚至只是浏览到恶意网站时,注入的代码就会传播到易受攻击的网站,从而将攻击反射回用户的浏览器。然后浏览器执行代码,因为它来自“受信任的”服务器。反射型 XSS 有时也称为非持久性或 II 型 XSS。
// 某网站具有搜索功能,该功能通过 URL 参数接收用户提供的搜索词:
https://xxx.com/search?query=123
// 服务器在对此 URL 的响应中回显提供的搜索词:
<p>您搜索的是: 123</p>
// 如果服务器不对数据进行转义等处理,则攻击者可以构造如下链接进行攻击:
https://xxx.com/search?query=<img src="https://juejin.cn/post/empty.png" onerror ="alert('xss')">
// 该 URL 将导致以下响应,并运行 alert('xss'):
<p>您搜索的是: <img src="empty.png" onerror ="alert('xss')"></p>
// 如果有用户请求攻击者的 URL ,则攻击者提供的脚本将在用户的浏览器中执行。
复制代码
DOM 型 XSS
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL。
- 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。
预防 DOM 型攻击 ⛳
DOM 型 XSS 攻击,实际上就是网站前端 JavaScript 代码本身不够严谨,把不可信的数据当作代码执行了。
在使用 .innerHTML、.outerHTML、document.write() 时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用 .textContent、.setAttribute() 等。
如果用 Vue/React 技术栈,并且不使用 v-html/dangerouslySetInnerHTML 功能,就在前端 render 阶段避免 innerHTML、outerHTML 的 XSS 隐患。
DOM 中的内联事件监听器,如 location、onclick、onerror、onload、onmouseover 等,<a> 标签的 href 属性,JavaScript 的 eval()、setTimeout()、setInterval() 等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易产生安全隐患,请务必避免。