反射式 XSS 的解释及其防御

2,481

编译自:blog.sqreen.io/reflected-x…

原作者:JB  发表时间:2018-03-08

译者:西楼听雨


什么是反射式跨站脚本攻击(reflected XSS)?

首先,我们来了解下什么是"跨站脚本攻击(XSS)":它是通过向网站注入恶意代码实现的,当某个用户访问受感染的页面时,脚本就会在其浏览器中执行,这时,攻击者就可以窃取用户的私有信息,例如,cookie、账户信息;也可以以受害者的身份执行一些恶意操作。

反射式跨站脚本攻击(也叫做非存储式跨站脚本攻击 (non-persistent XSS))是一种特殊的跨站脚本攻击,这种攻击,是将恶意脚本从一个网站反弹出来,通常是通过URL的query传递的,只需要用户点击一个链接就可达到。

和储存式跨站脚本攻击(stored XSS)相比,非存储式跨站脚本攻击只需要将恶意代码添加到链接上,然后欺骗、诱导用户点击即可。


为什么需要对反射式 XSS 留意?

因为,虽然反射式 XSS 对于攻击者来说力度较弱,但它相对于存储式 XSS 却更常见,也更容易实现,它只需要用户点击恶意链接即可达到,因此非常容易在邮件,论坛中部署;而且只要攻击者成功部署,他们仍然可以执行任意的 javascript。这就等于把所有可编程式地触发的操作全部给了攻击者。例如,可以把比特币用户的比特币与任意用户进行交易。

另外,攻击者也可以借助在浏览器中执行恶意代码的机会,来进一步利用基于浏览器、操作系统、浏览器插件的漏洞,以此获取用户电脑的操控权——通常会把其变为僵尸网络中的一员。

而且由于反射式 XSS 通常是通过采用社会学工程学来诱导用户点击恶意链接,例如,制作一个假冒的登陆页面,这种性质,使得它从用户的角度看,不管是HTTPS,URL,甚至是密码管理器都会把其视为正常情况并自动填写表单。

如何消除 XSS 漏洞?

XSS 漏洞通常是由于在渲染模板中缺少对用户提供的数据进行转义所致的,例如用户在输入框控件中的输入。下面是一个简单的例子,它是一个典型的 Runy on Rails 模板,里面有来自不同地方的由用户提供的数据。(其他语言中的引擎也是一个道理)

<div>
<h1>Blog post: <%= @post.title %></h1> (1)
<br />
<a href=“<%= @post.url %>”>Click here to see the full story</a> (2)
<script>
record_post_view(@post.id); (3)
</script>
<div id=“footer” <%= @post.footer_attr %>>&copy; 2018</div> (4)
</div>

在这个例子中有四个数据点,他们需要进行不同类型的转义。

这四个点都有发生 XSS 的可能,下面这张表格是注入 alert(0) 的方式及措施。

变量注入代码Rails 的转义函数
@post.titlescript>alert(0);</scriptERB::Util.html_escape
@post.url“ onblur=javascript:alert(0) “ or “>ERB::Util.html_escape
@post.id123); alert(0);无对应函数 – 确保 @post.id 为整数即可
@post.footer_attronblur=javascript:alert(0)无对应函数 – 确保用户不注入任何属性即可

所以对数据进行转义才是关键所在。这要求开发者在模板中插入数据时记得对数据进行转义。

如何实时地阻止 XSS?

老旧的方式——网络防火墙

标准 "Web应用防火墙”(WAF)及“下一代 Web 应用防火墙”的工作原理都一样,都是通过探测——观察网络数据——来阻止攻击者。他们的工作任务就是:尝试对入站的HTTP请求进行匹配。

但是 WAF 存在几个问题:首先你得把你的所有流量进行重定向,从隐私和延迟角度看,这并不友好;然后如果在已有的 XSS 指纹库(signature database)中无法找到匹配的信息,它就会被漏掉。它也永远无法做到涵盖最新的指纹,也很容易被绕过;而且误报的情况过多,使得它无论是在保护模式还是监视模式下使用起来都很痛苦。

所以像这种从应用外部来阻止攻击的方式其实并没有什么意义。就像是,你在对一个请求的任何技术细节都无法掌握到的情况下,你会通过一个外部的“探测器”来对其中某一个特定的部分做性能监控吗?

正确的方式——从应用内部检测攻击

和其他 Web 应用中的漏洞一样,很难找到一种比在应用本身内部进行观察更可靠的方式来阻止这样一种攻击。

上一个例子中,@post.title 变量并没有被转义,所以如果这个变量从 URL 进行了反射,这时就会出现反射式 XSS:

<h1>Blog post: <%= raw @post.title %></h1>

为了检测到这样一个未进行转义的数据,你需要在模板引擎中挂入一个钩子,对来自客户端的,如,URL参数、HTTP请求头,以及请求体进行检测,这样你才能精准地发现反射式 XSS,而且不会有像上面提到的误报的情况。

其实这正是 Sqreen 的工作原理,因为我们处于应用之内,我们可以检测到未转义的数据。

当 Sqreen 检测到在模板中有未转义的部分使用了来自用户提供的参数时,你可以决定对其进行哪些处理,比如:

——阻止请求

——(在运行时)对可执行的那部分代码进行转义

——把调用栈信息发送给开发人员

——POST 到 webhook

——发送通知到 slack 或者 Pagerduty

这就是 Sqreen 如何保护应用程序免受反射式 XSS 攻击的方式。

如果你有使用 Vue.js 并且对如何在 Vue 中防御 XSS 感兴趣,你可以看下我们之前的文章