认证与授权是不一样的。认证Authentication的目的是为了认出用户是谁,实际上就是一个验证凭证地过程;授权Authorization的目的是为了决定用户能够做什么。

如果一个凭证被用于认证,则成为“单因素认证”;如果有两个或多个凭证被用于认证,则成为“双因素(Two Factors)认证”或“多因素认证”。

 

密码

密码是最常见的一种认证手段,有点是使用成本低,认证过程实现起来简单;缺点是密码认证是一种比较弱的安全方案,可能会被猜解,要实现一个足够安全的密码方案,也不是一件轻松的事。

目前常用的一种暴力破解手段,不是破解密码,而是选择一些弱口令,比如123456,然后猜解用户名,直到发现一个使用弱口令地账户为止。

密码的保存也是需要注意的:必须以不可逆地加密算法,或者是单向散列函数算法,加密后存储在数据库中。明文保存密码的后果是很严重的,因为这些密码可用于尝试登陆其他网站。

所以,将明文密码经过哈希后(比如MD5或者SHA-1)在保存到数据库中,是目前业界比较普遍的做法——在用户注册时就已将密码哈希后保存再数据库中,登陆时验证密码的过程仅仅是验证用户提交的“密码”哈希值,与保存在数据库中的“密码”哈希值是否一致。

而破解MD5后密码的方法比较常用的是“彩虹表(Rainbow Table)”:

彩虹表的思路是收集尽可能多的密码明文和明文对应的MD5值。这样只需要查询MD5值,就能找到该MD5值对应的明文。一个好的彩虹表,可能会非常庞大。彩虹表的建立,还可以周期性地计算一些数据的MD5值,以扩充彩虹表的内容。

接下来就可以引出盐值salt地作用了:

为了避免通过彩虹表,从泄露的哈希值查询出明文,在计算密码明文的哈希值时,增加一个“Salt”。“Salt”是一个字符串,它的作用是为了增加明文的复杂度,并能使的彩虹表一类的攻击失效。

MD5(Username+Password+Salt)

其中Salt为随机字符串,应当被保存在服务器的配置文件中。

 

多因素认证:

除了密码外,手机动态口令、数字证书、第三方证书等等都可用于用户认证,还可以将这些因素相互结合使得认证的过程更加安全。

多因素认证提高了攻击的门槛。

 

Session与认证:

登陆完成后,用户访问网站的页面,不可能每次浏览器请求页面时都再使用密码认证一次。因此,当认证成功后,就需要替换一个对用户透明的凭证,就是SessionID。

当用户登录完成后,在服务器端就会创建一个新的会话(Session),会话中会保存用户的状态和相关信息。服务器端维护所有在线用户的Session,此时的认证,只需要知道是哪个用户在浏览当前的页面即可。为了告诉服务器应该使用哪一个Session,浏览器需要把当前用户持有的SessionID告知服务器。

最常见的做法就是把SessionID加密后保存在Cookie中,因为Cookie会随着HTTP请求头发送,且受到浏览器同源策略的保护。

所以,SessionID一旦在生命周期内被窃取,就等同于账户失窃。

Session劫持就是一种通过窃取用户SessionID后,使用该SessionID登录进目标账户的攻击方法,此时攻击者实际上是使用了目标账户的有效Session。如果SessionID是保存在Cookie中的,则这种攻击可以称为Cookie劫持。

SessionID除了可以保存在Cookie中外,还可以保存在URL中,作为请求的一个参数。但是不安全。在手机操作系统中,有很多手机浏览器暂不支持Cookie,所以只能将SessionID作为URL的一个参数用于认证。

比如当前访问的页面URL中含有sid,页面源码中一个img标签:

<img src="http://www.inbreak.net/logo.php">

浏览器在解析图片时会发送一次GET请求,这个请求会带上Referer,Referer的值就会含有sid。那个www.inbreak.net服务器的日志中可以查询到sid了。

在生成SessionID时,需要保证足够的随机性,比如采用足够强的伪随机数生成算法。

 

Session Fixation攻击:

在用户登陆网站的过程中,如果登陆前后用户的SessionID没有发生变化,则会存在Session Fixation问题。

攻击者先获取一个未经认证的SessionID,然后将这个SessionID交给受害者去认证。受害者完成认证后,服务器并没有更新SessionID的值(注意是未改变SessionID,而不是未改变Session),所以攻击者可以直接凭借此SessionID登录进受害者的账户。

那么问题就是攻击者如何让受害者使用这个SessionID呢。如果SessionID保存在Cookie中就比较难以做到;但如果SessionID保存在URL中,攻击者只要诱使受害者打开这个URL即可。

比如,认证前的URL是:

http://bbs.xxxx.com/wap/index.php?action=forum&fid=72&sid=2iu2pf

其中,sid是用于认证的SessionID。用户登录后,这个sid没有发生改变,因此攻击者可以先构造好此URL,并诱使其他用户打开,当用户登陆完成后,攻击者也可以直接通过此URL进入用户账户。

解决SessionID Fixation的正确做法是,在登陆完成后,重写SessionID。

 

SessionID保持攻击:

一般来说,Session是有生命周期的,当用户长时间未活动后,或者用户点击退出后,服务器将销毁Session。

但如果攻击者能一直持有一个有效的Session(比如间隔性地刷新页面,以告诉服务器这个用户仍然在活动),而服务器对于活动的Session也一直不销毁的话,攻击者就能通过此有效Session一直使用用户的账户,成为一个永久的“后门”。

所以,一般的应用都会给session设置一个失效时间。但是也有应用为了用户体验,只要用户还“活着”,Session就不会失效。

保持Session:

<script>
//下面是要保持session的地址。
var url="http://bbs.ecshop.com/wap/index.php?sid=loALS7";
window.setInterval("keepsid()",60000);
function keepsid(){
document.getElementById("iframe1").src=url+"&time="+Math.random();
}
</script>
<iframe id="iframe1" src=""></iframe>

其原理就是不停地刷新页面,以保持Session不过期。

而Cookie是可以完全由客户端控制的,通过发送带有自定义Cookie头的HTTP包,也能实现同样的效果。

想使得Cookie不失效,还有更简单的方法:

在Web开发中,网站访问量如果比较大,维护Session可能会给网站带来巨大负担。因此,有一种做法,就是服务器端不维护Session,而把Session放在Cookie中加密保存。当浏览器访问网站时,会自动带上Cookie,服务器端只需要解密Cookie即可得到当前用户的Session了。这样的Session如何使其过期呢?很多应用都是利用Cookie的Expire标签来控制Session的失效时间,这就给了攻击者可乘之机。

Cookie的Expire时间完全可以由客户端控制。篡改这个时间,并使之永久有效,就有可能获得一个永久有效的Session,而服务器端是完全无法察觉的。

例如:在XSS攻击后将Cookie设置为永不过期(由JavaScript实现):

//让一个Cookie不过期
anehta.dom.persistCookie=function(cookieName){
    if(anehta.dom.checkCookie(cookieName)==false){
        return galse;
    }

    try{
   document.cookie=cookieName+"="+anehta.dom.getCookie(cookieName)+";"+"expires=Thu,01-Jan-2038 00:00:01 GMT;";
    }catech(e){
        return false;
    }
        return true;
}

攻击者甚至可以为Session Cookie增加一个Expire时间,使得原本浏览器关闭就会失效的Cookie持久化地保存在本地,变成一个第三方Cookie(third-party cookie)。

那么如何解决这种Session保持攻击呢?

 可以强制销毁Session,比如从用户登录起的时间算起,三天后强制Session过去。

但是这样会影响一些正常用户的体验。还有一个解决方法是当用户客户端发生变化时,比如用户的IP、UserAgent等信息发生了变化时,就可以强制销毁当前的Session,并要求用户重新登陆。

最后,还需要考虑的是同一用户可以同时拥有几个有效的Session。若为1个,则Session保持攻击难以实现,因为会被正常用户的登录踢出。

 

单点登录(SSO):Single Sign On

单点登录是希望用户只需要登陆一次,就可以访问所有的系统。

SSO的优点在于风险集中化,就只需要保护好这一点。它把用户的登录过程集中在一个地方。在单点处设计安全方案,甚至可以考虑使用一些较“重”的方法,比如双因素认证。

SSO缺点是如果单点被攻破,后果会非常严重。降低这种风险的方法是在一些敏感的系统里,再单独实现一些额外的认证机制。比如付款前的支付密码、手机短信验证身份。

目前互联网上最为开放和流行的单点登陆系统是OpenID。OpenID是一个开放的单点登录框架它希望使用URI作为用户在互联网上的身份标识,每个用户(End User)将拥有一个唯一的URI。在用户登陆网站(Relying Party)时,用户只需要提交他的OpenID(就是用户唯一的URI)以及OpenID的提供者(OpenID Provider),网站就会将用户重定向到OpenID的提供者进行认证,认证完成后再重定向回网站。

 

参考书籍:《白帽子讲Web安全》