HTTP请求报头: Authorization
HTTP响应报头: WWW-Authenticate
HTTP认证是基于质询/回应(challenge/response)的认证模式。
1 基本认证 basic authentication(HTTP1.0提出的认证方法)
基本认证是一种用来允许Web浏览器或其他客户端程序在请求时提供用户名和口令形式的身份凭证的一种登录验证方式。
把 "用户名+冒号+密码"用BASE64算法加密后的字符串放在http request 中的header Authorization中发送给服务端。
客户端对于每一个realm,通过提供用户名和密码来进行认证的方式。
包含密码的明文传递。
当浏览器访问使用基本认证的网站的时候, 浏览器会提示你输入用户名和密码,如下图:
假如用户名密码错误的话,服务器会返回401,如下图:
基本认证步骤:
1、客户端访问一个受http基本认证保护的资源。
2、服务器返回401状态,要求客户端提供用户名和密码进行认证。(验证失败的时候,响应头会加上WWW-Authenticate: Basic realm="请求域"。)
401 Unauthorized
WWW-Authenticate: Basic realm="WallyWorld"
3、客户端将输入的用户名密码用Base64进行编码后,采用非加密的明文方式传送给服务器。
Authorization: Basic xxxxxxxxxx.
4、服务器将Authorization头中的用户名密码解码并取出,进行验证,如果认证成功,则返回相应的资源。如果认证失败,则仍返回401状态,要求重新进行认证。
特记事项:
1、Http是无状态的,同一个客户端对同一个realm内资源的每一个访问会被要求进行认证。
2、客户端通常会缓存用户名和密码,并和authentication realm一起保存,所以,一般不需要你重新输入用户名和密码。
3、以非加密的明文方式传输,虽然转换成了不易被人直接识别的字符串,但是无法防止用户名密码被恶意盗用。虽然用肉眼看不出来,但用程序很容易解密。
优点:
基本认证的一个优点是基本上所有流行的网页浏览器都支持基本认证。基本认证很少在可公开访问的互联网网站上使用,有时候会在小的私有系统中使用(如路由器
网页管理接口)。后来的机制HTTP摘要认证是为替代基本认证而开发的,允许密钥以相对安全的方式在不安全的通道上传输。
程序员和系统管理员有时会在可信网络环境中使用基本认证,使用Telnet或其他明文网络协议工具手动地测试Web服务器。这是一个麻烦的过程,但是网络上传输的
内容是人可读的,以便进行诊断。
缺点:
虽然基本认证非常容易实现,但该方案建立在以下的假设的基础上,即:客户端和服务器主机之间的连接是安全可信的。特别是,如果没有使用SSL/TLS这样的传输
层安全的协议,那么以明文传输的密钥和口令很容易被拦截。该方案也同样没有对服务器返回的信息提供保护。
现存的浏览器保存认证信息直到标签页或浏览器被关闭,或者用户清除历史记录。HTTP没有为服务器提供一种方法指示客户端丢弃这些被缓存的密钥。这意味着服务
器端在用户不关闭浏览器的情况下,并没有一种有效的方法来让用户登出。
一个例子:
这一个典型的HTTP客户端和HTTP服务器的对话,服务器安装在同一台计算机上(localhost),包含以下步骤:
客户端请求一个需要身份认证的页面,但是没有提供用户名和口令。这通常是用户在地址栏输入一个URL,或是打开了一个指向该页面的链接。服务端响应一个401应
答码,并提供一个认证域。接到应答后,客户端显示该认证域(通常是所访问的计算机或系统的描述)给用户并提示输入用户名和口令。此时用户可以选择确定或取
消。用户输入了用户名和口令后,客户端软件会在原先的请求上增加认证消息头(值是base64encode(username+":"+password)),然后重新发送再次尝试。
在本例中,服务器接受了该认证屏幕并返回了页面。如果用户凭据非法或无效,服务器可能再次返回401应答码,客户端可以再次提示用户输入口令。
注意:客户端有可能不需要用户交互,在第一次请求中就发送认证消息头。
客户端请求(没有认证信息):
GET /private/index.html HTTP/1.0
Host: localhost
(跟随一个换行,以回车(CR)加换行(LF)的形式)
服务端应答:
HTTP/1.0 401 Authorization Required
Server: HTTPd/1.0
Date: Sat, 27 Nov 2004 10:18:15 GMT
WWW-Authenticate: Basic realm="Secure Area"
Content-Type: text/html
Content-Length: 311
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"><HTML>
<HEAD>
<TITLE>Error</TITLE>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">
</HEAD>
<BODY><H1>401 Unauthorized.</H1></BODY></HTML>
客户端的请求(用户名“"Aladdin”,口令, password “open sesame”):
GET /private/index.html HTTP/1.0
Host: localhost
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
(跟随一个空行,如上所述)
服务端的应答:
HTTP/1.0 200 OK
Server: HTTPd/1.0
Date: Sat, 27 Nov 2004 10:19:07 GMT
Content-Type: text/html
Content-Length: 10476
(跟随一个空行,随后是需凭据页的HTML文本)。
HTTP OAuth认证
OAuth对于Http来说,就是放在Authorization header中的不是用户名密码, 而是一个token。微软的Skydrive就是使用这样的方式。
2、摘要认证 digest authentication(HTTP1.1提出的基本认证的替代方法)
这个认证可以看做是基本认证的增强版本,不包含密码的明文传递。
引入了一系列安全增强的选项;“保护质量”(qop)、随机数计数器由客户端增加、以及客户生成的随机数。
在HTTP摘要认证中使用 MD5 加密是为了达成"不可逆的",也就是说,当输出已知的时候,确定原始的输入应该是相当困难的。如果密码本身太过简单,也许可以
通过尝试所有可能的输入来找到对应的输出(穷举攻击),甚至可以通过字典或者适当的查找表加快查找速度。
示例及说明
下面的例子仅仅涵盖了“auth”保护质量的代码,因为在撰写期间,所知道的只有Opera和Konqueror网页浏览器支持“auth-int”(带完整性保护的认证)。
典型的认证过程包括如下步骤:
客户端请求一个需要认证的页面,但是不提供用户名和密码。通常这是由于用户简单的输入了一个地址或者在页面中点击了某个超链接。
服务器返回401 "Unauthorized" 响应代码,并提供认证域(realm),以及一个随机生成的、只使用一次的数值,称为密码随机数 nonce。
此时,浏览器会向用户提示认证域(realm)(通常是所访问的计算机或系统的描述),并且提示用户名和密码。用户此时可以选择取消。
一旦提供了用户名和密码,客户端会重新发送同样的请求,但是添加了一个认证头包括了响应代码。
注意:客户端可能已经拥有了用户名和密码,因此不需要提示用户,比如以前存储在浏览器里的。
客户端请求 (无认证):
GET /dir/index.html HTTP/1.0
Host: localhost
(跟随一个新行,形式为一个回车再跟一个换行)
服务器响应:
HTTP/1.0 401 Unauthorized
Server: HTTPd/0.9
Date: Sun, 10 Apr 2005 20:26:47 GMT
WWW-Authenticate: Digest realm="testrealm@host.com", //认证域
qop="auth,auth-int", //保护质量
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", //服务器密码随机数
opaque="5ccc069c403ebaf9f0171e9517f40e41"
Content-Type: text/html
Content-Length: 311<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"><HTML>
<HEAD>
<TITLE>Error</TITLE>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">
</HEAD>
<BODY><H1>401 Unauthorized.</H1></BODY></HTML>
客户端请求 (用户名 "Mufasa", 密码 "Circle Of Life"):
GET /dir/index.html HTTP/1.0
Host: localhost
Authorization: Digest username="Mufasa",
realm="testrealm@host.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
uri="/dir/index.html",
qop=auth,
nc=00000001, //请求计数
cnonce="0a4f113b", //客户端密码随机数
response="6629fae49393a05397450978507c4ef1",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
(跟随一个新行,形式如前所述)。
服务器响应:
HTTP/1.0 200 OK
Server: HTTPd/0.9
Date: Sun, 10 Apr 2005 20:27:03 GMT
Content-Type: text/html
Content-Length: 7984
(随后是一个空行,然后是所请求受限制的HTML页面)
response 值由三步计算而成。当多个数值合并的时候,使用冒号作为分割符:
1、对用户名、认证域(realm)以及密码的合并值计算 MD5 哈希值,结果称为 HA1。
2、对HTTP方法以及URI的摘要的合并值计算 MD5 哈希值,例如,"GET" 和 "/dir/index.html",结果称为 HA2。
3、对HA1、服务器密码随机数(nonce)、请求计数(nc)、客户端密码随机数(cnonce)、保护质量(qop)以及 HA2 的合并值计算 MD5 哈希值。结果即为客户端提供的
response 值。
因为服务器拥有与客户端同样的信息,因此服务器可以进行同样的计算,以验证客户端提交的 response 值的正确性。在上面给出的例子中,结果是如下计算的。
(MD5()表示用于计算MD5哈希值的函数;“\”表示接下一行;引号并不参与计算)
HA1 = MD5( "Mufasa:testrealm@host.com:Circle Of Life" )
= 939e7578ed9e3c518a452acee763bce9
HA2 = MD5( "GET:/dir/index.html" )
= 39aff3a2bab6126f332b942af96d3366
Response = MD5( "939e7578ed9e3c518a452acee763bce9:\
dcd98b7102dd2f0e8b11d0f600bfb0c093:\
00000001:0a4f113b:auth:\
39aff3a2bab6126f332b942af96d3366" )
= 6629fae49393a05397450978507c4ef1
此时客户端可以提交一个新的请求,重复使用服务器密码随机数(nonce)(服务 器仅在每次“401”响应后发行新的nonce),但是提供新的客户端密码随机数(cnonce)。在后续的请求中,十六进制请求计数器(nc)必须比前 一次使用的时候要大,否则攻击者可以简单的使用同样的认证信息重放老的请求。由服务器来确保在每个发出的密码随机数nonce时,计数器是在增加的,并拒 绝掉任何错误的请求。显然,改变HTTP方法和/或计数器数值都会导致不同的 response值。
服务器应当记住最近所生成的服务器密码随机数nonce的值。也可以在发行每一个密码随机数nonce后,记住过一段时间让它们过期。如果客户端使用了一个过期的值,服务器应该响应“401”状态号,并且在认证头中添加stale=TRUE,表明客户端应当使用新提供的服务器密码随机数nonce重发请求,而不必提示用户其它用户名和口令。
服务器不需要保存任何过期的密码随机数,它可以简单的认为所有不认识的数值都是过期的。服务器也可以只允许每一个服务器密码随机数nonce使用一次,当然,这样就会迫使客户端在发送每个请求的时候重复认证过程。需要注意的是,在生成后立刻过期服务器密码随机数nonce是不行的,因为客户端将没有任何机会来使用这个nonce。