WEB安全----XSS和CSRF

随着Web2.0、社交网络、微博等等一系列新型的互联网产品的诞生,基于Web环境的互联网应用愈来愈普遍,企业信息化的过程当中各类应用都架设在Web平台上,Web业务的迅速发展也引发黑客们的强烈关注,接踵而至的就是Web安全威胁的凸显。javascript

黑客利用网站操做系统的漏洞和Web服务程序的SQL注入漏洞等获得Web服务器的控制权限,轻则篡改网页内容,重则窃取重要内部数据,更为严重的则是在网页中植入恶意代码,使得网站访问者受到侵害。php

现在,Web安全成为焦点,但网站的漏洞仍是频频出现,在白帽子们进行网站测试时,恐怕对于SQL注入、XSS跨站、CSRF接触最多,但对于网站的开发者们来讲,对这些熟知多少?本文从开发者的角度,对于XSS和CSRF进行简要概述。html

PART1  XSS跨站脚本(Cross-site scripting)java

XSS成因归纳 :node

    XSS其实就是Html的注入问题,攻击者的输入没有通过严格的控制进入了数据库,最终显示给来访的用户,致使能够在来访用户的浏览器里以浏览用户的身份执行Html代码,数据流程以下:攻击者的Html输入—>web程序—>进入数据库—>web程序—>用户浏览器。web

检测方法:ajax

//一般有一些方式能够测试网站是否有正确处理特殊字符:数据库

><script>alert(document.cookie)</script> ='><script>alert(document.cookie)</script> "><script>alert(document.cookie)</script> <script>alert(document.cookie)</script> <script>alert(vulnerable)</script> %3Cscript%3Ealert('XSS')%3C/script%3E <script>alert('XSS')</script> <img src="javascript:alert('XSS')"> <img src="http://xxx.com/yyy.png" onerror="alert('XSS')"> <div style="height:expression(alert('XSS'),1)" />(这个仅限 IE 有效)

攻击手段和目的:express

    攻击者使被攻击者在浏览器中执行脚本后,若是须要收集来自被攻击者的数据(如cookie或其余敏感信息),能够自行架设一个网站,让被攻击者经过JavaScript等方式把收集好的数据做为参数提交,随后以数据库等形式记录在攻击者本身的服务器上。 浏览器

a. 盗用 cookie ,获取敏感信息。

b.利用植入 Flash ,经过 crossdomain 权限设置进一步获取更高权限;或者利用Java等获得相似的操做。 

c.利用 iframe、frame、XMLHttpRequest或上述Flash等方式,以(被攻击)用户的身份执行一些管理动做,或执行一些通常的如发微博、加好友、发私信等操做。 

d.利用可被攻击的域受到其余域信任的特色,以受信任来源的身份请求一些平时不容许的操做,如进行不当的投票活动。

e.在访问量极大的一些页面上的XSS能够攻击一些小型网站,实现DDoS攻击的效果。

漏洞的防护和利用:

避免XSS的方法之一主要是将用户所提供的内容进行过滤,许多语言都有提供对HTML的过滤:

PHP的htmlentities()或是htmlspecialchars()。
Python的cgi.escape()。
ASP的Server.HTMLEncode()。
ASP.NET的Server.HtmlEncode()或功能更强的Microsoft Anti-Cross Site Scripting Library
Java的xssprotect(Open Source Library)。
Node.js的node-validator。

使用HTTP头指定类型:

不少时候可使用HTTP头指定内容的类型,使得输出的内容避免被做为HTML解析。如在PHP语言中使用如下代码: 

header
('Content-Type: text/javascript; charset=utf-8');

 

便可强行指定输出内容为文本/JavaScript脚本(顺便指定了内容编码),而非能够引起攻击的HTML。

PART2 CSRF:冒充用户之手

示意图:

    XSS 是实现 CSRF 的诸多途径中的一条,但绝对不是惟一的一条。通常习惯上把经过 XSS 来实现的 CSRF 称为 XSRF。

    CSRF 顾名思义,是伪造请求,冒充用户在站内的正常操做。咱们知道,绝大多数网站是经过 cookie 等方式辨识用户身份(包括使用服务器端 Session 的网站,由于 Session ID 也是大多保存在 cookie 里面的),再予以受权的。因此要伪造用户的正常操做,最好的方法是经过 XSS 或连接欺骗等途径,让用户在本机(即拥有身份 cookie 的浏览器端)发起用户所不知道的请求。 

    要完成一次CSRF攻击,受害者必须依次完成两个步骤:

1.登陆受信任网站A,并在本地生成Cookie。
2.在不登出A的状况下,访问危险网站B。

    看到这里,你也许会说:“若是我不知足以上两个条件中的一个,我就不会受到CSRF的攻击”。是的,确实如此,但你不能保证如下状况不会发生:
1.你不能保证你登陆了一个网站后,再也不打开一个tab页面并访问另外的网站。
2.你不能保证你关闭浏览器了后,你本地的Cookie马上过时,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登陆/结束会话了……)
3.上图中所谓的攻击网站,多是一个存在其余漏洞的可信任的常常被人访问的网站。
    上面大概地讲了一下CSRF攻击的思想,下面我将用几个例子详细说说具体的CSRF攻击,这里我以一个银行转帐的操做做为例子(仅仅是例子,真实的银行网站没这么傻:>)

示例1:
银行网站A,它以GET请求来完成银行转帐的操做,如:

http://www.mybank.com/Transfer.php?toBankId=11&money=1000

危险网站B,它里面有一段HTML的代码以下:

 <img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>

    首先,你登陆了银行网站A,而后访问危险网站B,噢,这时你会发现你的银行帐户少了1000块……
    为何会这样呢?缘由是银行网站A违反了HTTP规范,使用GET请求更新资源。在访问危险网站B的以前,你已经登陆了银行网站A,而B中的<img>以GET的方式请求第三方资源(这里的第三方就是指银行网站了,本来这是一个合法的请求,但这里被不法分子利用了),因此你的浏览器会带上你的银行网站A的Cookie发出Get请求,去获取资源“http://www.mybank.com/Transfer.php?toBankId=11&money=1000”,结果银行网站服务器收到请求后,认为这是一个更新资源操做(转帐操做),因此就马上进行转帐操做……

示例2:
为了杜绝上面的问题,银行决定改用POST请求完成转帐操做。
银行网站A的WEB表单以下:  

<form action="Transfer.php" method="POST">
        <p>ToBankId: <input type="text" name="toBankId" /></p>
        <p>Money: <input type="text" name="money" /></p>
        <p><input type="submit" value="Transfer" /></p>
</form>

 

后台处理页面Transfer.php以下:

<?php
      session_start();
    if (isset($_REQUEST[&#039;toBankId&#039;] && isset($_REQUEST[&#039;money&#039;]))
    {
       buy_stocks($_REQUEST[&#039;toBankId&#039;], $_REQUEST[&#039;money&#039;]);
    }
>

 

危险网站B,仍然只是包含那句HTML代码:

<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>

    和示例1中的操做同样,你首先登陆了银行网站A,而后访问危险网站B,结果…..和示例1同样,你再次没了1000块~T_T,此次事故的缘由是:银行后台使用了$_REQUEST去获取请求的数据,而$_REQUEST既能够获取GET请求的数据,也能够获取POST请求的数据,这就形成了在后台处理程序没法区分这究竟是GET请求的数据仍是POST请求的数据。在PHP中,可使用$_GET和$_POST分别获取GET请求和POST请求的数据。在JAVA中,用于获取请求数据request同样存在不能区分GET请求数据和POST数据的问题。 

示例3:
    通过前面2个惨痛的教训,银行决定把获取请求数据的方法也改了,改用$_POST,只获取POST请求的数据,后台处理页面Transfer.php代码以下:

<?php
     session_start();
     if (isset($_POST['toBankId'] && isset($_POST['money']))
     {
        buy_stocks($_POST['toBankId'], $_POST['money']);
     }
?>

 

然而,危险网站B与时俱进,它改了一下代码:

<html>
      <head>
        <script type="text/javascript">
          function steal()
          {
                   iframe = document.frames["steal"];
                   iframe.document.Submit("transfer");
          }
        </script>
      </head>
      <body onload="steal()">
        <iframe name="steal" display="none">
          <form method="POST" name="transfer" action="http://www.myBank.com/Transfer.php">
            <input type="hidden" name="toBankId" value="11">
            <input type="hidden" name="money" value="1000">
          </form>
        </iframe>
      </body>
</html>

 

    若是用户还是继续上面的操做,很不幸,结果将会是再次不见1000块……由于这里危险网站B暗地里发送了POST请求到银行!

    总结一下上面3个例子,CSRF主要的攻击模式基本上是以上的3种,其中以第1,2种最为严重,由于触发条件很简单,一个<img>就能够了,而第3种比较麻烦,须要使用JavaScript,因此使用的机会会比前面的少不少,但不管是哪一种状况,只要触发了CSRF攻击,后果都有可能很严重。
    理解上面的3种攻击模式,其实能够看出,CSRF攻击是源于WEB的隐式身份验证机制!WEB的身份验证机制虽然能够保证一个请求是来自于某个用户的浏览器,但却没法保证该请求是用户批准发送的!

如何防护?
请求令牌(一种简单有效的防护方法):
首先服务器端要以某种策略生成随机字符串,做为令牌(token),保存在 Session 里。而后在发出请求的页面,把该令牌以隐藏域一类的形式,与其余信息一并发出。在接收请求的页面,把接收到的信息中的令牌与 Session 中的令牌比较,只有一致的时候才处理请求,处理完成后清理session中的值,不然返回 HTTP 403 拒绝请求或者要求用户从新登录验证身份 
令牌来防止 CSRF 有如下几点要注意:
 a.虽然请求令牌原理和验证码有类似之处,但不该该像验证码同样,全局使用一个 Session Key。由于请求令牌的方法在理论上是可破解的,破解方式是解析来源页面的文本,获取令牌内容。若是全局使用一个 Session Key,那么危险系数会上升。原则上来讲,每一个页面的请求令牌都应该放在独立的 Session Key 中。咱们在设计服务器端的时候,能够稍加封装,编写一个令牌工具包,将页面的标识做为 Session 中保存令牌的键。

b.在 ajax 技术应用较多的场合,由于颇有请求是 JavaScript 发起的,使用静态的模版输出令牌值或多或少有些不方便。但不管如何,请不要提供直接获取令牌值的 API。这么作无疑是锁上了大门,却又把钥匙放在门口,让咱们的请求令牌退化为同步令牌。

c.第一点说了请求令牌理论上是可破解的,因此很是重要的场合,应该考虑使用验证码(令牌的一种升级,目前来看破解难度极大),或者要求用户再次输入密码(亚马逊、淘宝的作法)。但这两种方式用户体验都很差,因此须要产品开发者权衡。

d.不管是普通的请求令牌仍是验证码,服务器端验证过必定记得销毁。忘记销毁用过的令牌是个很低级可是杀伤力很大的错误。咱们学校的选课系统就有这个问题,验证码用完并未销毁,故只要获取一次验证码图片,其中的验证码能够在屡次请求中使用(只要再也不次刷新验证码图片),一直用到。

以下也列出一些听说能有效防范 CSRF,其实效果甚微或甚至无效的作法:
a.经过 referer 断定来源页面:referer 是在 HTTP Request Head 里面的,也就是由请求的发送者决定的。若是我喜欢,能够给 referer 任何值。固然这个作法并非毫无做用,起码能够防小白。但我以为性价比不如令牌。

b.过滤全部用户发布的连接:这个是最无效的作法,由于首先攻击者不必定要从站内发起请求(上面提到过了),并且就算从站内发起请求,途径也远远不知连接一条。好比 <img src="./create_post.php" /> 就是个不错的选择,还不须要用户去点击,只要用户的浏览器会自动加载图片,就会自动发起请求。

c.在请求发起页面用 alert 弹窗提醒用户:这个方法看上去能干扰站外经过 iframe 发起的 CSRF,但攻击者也能够考虑用 window.alert = function(){}; 把 alert 弄哑,或者干脆脱离 iframe,使用 Flash 来达到目的。

整体来讲,目前防护 CSRF 的诸多方法还没几个能完全无解的。 做为开发者,咱们能作的就是尽可能提升破解难度。当破解难度达到必定程度,网站就逼近于绝对安全的位置了

相关文章
相关标签/搜索