松鼠书读书笔记(六)——认证

HTTP规范中有单独的一块,叫做“认证”。目的是对服务器的资源进行保护,只允许合法用户访问

一般我们自己开发的系统,也都会有认证机制,用户输入用户名和密码之后,才允许访问系统

但是这种认证是应用层面的认证,和HTTP的认证不是一回事,本文说的是HTTP协议层面的认证,不是WEB应用层面的认证

比如说,访问淘宝,需要输入用户名和密码,这是应用层面的认证;请求某个服务器通过http提供一份文档,server返回401,要求客户端提供用户名和密码,这是HTTP协议层面的认证

认证分为2种,一种是“基本认证”,另一种是“摘要认证”

一、基本认证

基本认证是非常简单的,举一个例子
GET /family/jeff.jpg HTTP/1.0

HTTP/1.0 401 Authorization Required
WWW-Authenticate:Basic realm="Family"

GET /family/jeff.jpg HTTP/1.0
Authorization:Basic fjsfieqe4jkvkldfe

HTTP/1.0 200 OK
......

流程非常简单,client按照“用户名:密码”的格式得到字符串,然后对此字符串进行BASE64编码,然后发到server

基本认证的安全性很低,只能说是聊胜于无,有以下几个主要问题:

1、在client和server中传输了密码,所以很容易被截获,比如经过proxy,这个proxy很容易就可以得到密码;或者用户不小心在浏览器上装了恶意插件,密码也就泄露了

2、密码等于是明文传输的,BASE64只是一种编码,不是加密,很容易就可以逆向算出密码

3、得到Authorization首部后,可以很容易地无限重发攻击

二、摘要认证

摘要认证比基本认证在安全性上要强一些,不过也有缺陷,所以还是不如HTTPS应用广泛,这里只简单了解一下。后一篇文章重点介绍目前最主流的安全方案HTTPS

也举个例子进行说明:
GET /family/jeff.jpg HTTP/1.0

HTTP/1.0 401 Unauthorized
WWW-Authenticate:Digest realm="Family" nonce="nonce123"

GET /family/jeff.jpg HTTP/1.0
Authorization:Digest username="bri" realm="Family" nonce="nonce123" response="MD5123456"

HTTP/1.0 200 OK
......

这里有所简化(没有包括预授权、保护质量协商等内容),但是已经可以说明摘要认证与基本认证的区别

这里传输的MD5123456,不是完整的密码,而是通过摘要算法(MD5)计算出的密码摘要。所以在网络上传输的,只是密码的摘要,而不是完整的密码,这样就避免了完整的密码被泄露

其次,MD5算法与BASE64不同,仅通过摘要字符串,很难逆向计算出原始字符串

并且,这里server发送Authorization首部时,还发送了一个nonce作为随机数,nonce要参与密码摘要的计算。由于每次的随机数都是不同的,所以计算得到的摘要结果也是不同的。这就避免了恶意用户截取到摘要结果后,发起重发攻击

这里要说明的是,认证(基本认证、摘要认证)是HTTP规范的一部分,具体实现依赖于server和client

比如说,有一些早期的浏览器,只实现了基本认证,而没有实现摘要认证,这是完全可能的。那么这种浏览器,就无法访问要求摘要认证的http server了

http认证,我认为只能是网络安全的一个补充,保护一些不太敏感的文档,还是有点作用的。因为你可以不需要在应用层面额外做编码工作,将认证的工作交给服务器,来节省一点工作量

但是现在的很多WEB应用,用HTTP认证的方式来保护是不可行的。比如说很多的B2C应用,如果用http认证,那很多只是想看看,懒得去注册的用户(比如我),肯定就会把网页关掉了

三、顺便说说密码明文传输的问题

我多年前做的一个内部OA系统,十分幼稚,用户密码都是明文保存在数据库里的。后来被主管批评以后,才知道这样是不对的,改用MD5算法,将密码摘要以后存在数据库里

但是这样做,仅仅是保证了,“如果database server被攻破”,用户的密码不会被泄露而已

如果攻击是发生在internet传输过程之中,还是防不胜防的

其实现在非常多的网站依然有这个问题,包括很大的知名B2C网站。就拿当当网举个例子:



这就是html里的一个普通的form,所以当用户填完用户名和密码,点击“登陆”按钮之后,浏览器就会发起一个post请求,把表单的内容放在http request的entity-body里发出去。这是浏览器行为,WEB应用是没有办法的

下面是我用chrome的开发者工具截取到的原始http request:

__VIEWSTATE:/wEPDwULLTE4ODM3NDAxMTFkZA==
txtUsername:[email protected](这是我在当当的注册邮箱)
txtPassword:xxxxxx(这是我在当当的注册密码)
login_type:0
A12B56CD78EF90G:ATkRHcR2iJwy2e1s13/1kg==
wdvalidatetoken:kW3+YmOUtaU=
hdn_returnurl:http%3a%2f%2fwww.dangdang.com%2f
btnSign:

由于现在大部分web应用的UI,还是基于HTML的,所以很难避免这个问题

一种办法,是把这个作为标准实现,浏览器遇到<input type="password">的HTML元素,对其进行某种加密;然后所有的服务端用同样的算法来解密

但是这种方法可行性极低,这个是html层面的事情,不可能写入到HTTP规范里。而且大部分的server也是HTTP server,并不是专门来处理html应用的

另一种办法,就是WEB应用自行开发浏览器安全输入的插件,比如说支付宝就是这么做的:



可以看到,上图并不是一个html表单,而是一个私有的控件,那么用户名和密码就可以在其中加密后传输了,到了支付宝的server再解密,避免了密码明文传输

猜你喜欢

转载自kyfxbl.iteye.com/blog/1772615