本文着重介绍Basic
、Digest
、FormBase
这三种认证方式
关于Basic认证及报文交互过程
关于Digest认证及摘要计算方法
关于FormBase认证及Session原理
HTTP/1.1的认证方式
Basic
(基础)认证Digest
(摘要)认证SSL
客户端认证FormBase
(基于表单)认证
Web服务器
- 报文字段:
WWW-Authenticate
: 要求认证Authorization
: (请求)认证的信息
- 状态码:
401 Unauthorized
代理服务器
- 报文字段:
Proxy-Authenticate
Proxy-Authorization
- 状态码:
407 Proxy Authentication Required
Basic认证
步骤
实践
工具:
http-server
telnet
设置参数:
ip
: localhostport
: 8082username
: foopassword
: bar
创建HTTP服务器
export NODE_HTTP_SERVER_USERNAME="foo"
export NODE_HTTP_SERVER_PASSWORD="bar"
http-server --port=8082
浏览器显示

报文首部
telnet localhost 8082
发送请求:
GET / HTTP/1.1
host: localhost
服务端返回401要求认证:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm=""
Date: Sun, 09 Feb 2020 18:31:27 GMT
Connection: keep-alive
Transfer-Encoding: chunked
将 base64(username:password),即b64(foo:bar)
(=Zm9vOmJhcg==
)发送:
GET / HTTP/1.1
host: localhost
Authorization: Basic Zm9vOmJhcg==
服务器认证成功后返回状态码200
HTTP/1.1 200 OK
server: ecstatic-3.3.2
cache-control: max-age=3600
last-modified: Thu, 30 Jan 2020 16:23:02 GMT
etag: W/"13372013-39766-2020-01-30T16:23:02.850Z"
content-length: 39766
content-type: text/html; charset=UTF-8
Date: Sun, 09 Feb 2020 18:31:31 GMT
Connection: keep-alive
虽然似乎只要在浏览器中登录认证一次,就可以直接访问其他同域资源。
但HTTP始终是一个无状态的协议(All requests and responses are independent),实际上每次请求都是需要认证身份的,只是浏览器已经根据缓存填好对应的认证信息了。
因为base64编码并不是加密处理,所以这种认证方式也并不安全,同时也不够灵活,所以不常用。
Digest认证
特点($md5(salt+passwd)$):
- 用摘要保护密码(MD5单向散列)
- Server产生随机数(对密码加盐)避免重放攻击(replay attack)
- 即攻击者用捕获的认证凭据之后重新向服务器认证
- Client产生随机数来支持对Server的认证
步骤
参数:
nonce
: 一次性的随机字符串 [server –> client] (防止重放攻击)cnonce
(client nonce): 随机字符串 [client –> server]nonceCount
: 请求次数(进一步防止重放攻击)qop
: 质量保护(影响摘要算法的计算方式)algorithm
: 影响摘要计算方式rspauth
: server的应答摘要(用于让client验证server的身份)
摘要计算
计算参数:
username
password
HA1
: 字符串变量(中间参数)HA2
response
: 计算结果
if `algorithm` is "MD5" or unspecified then
`HA1` = MD5(username:realm:password)
else if `algorithm` is "MD5-sess" then
`HA1` = MD5(MD5(username:realm:password):nonce:cnonce)
if `qop` is "auth" or unspecified then
`HA2` = MD5(method:digestURI)
else if `qop` is "auth-int" then
`HA2` = MD5(method:digestURI:MD5(entityBody))
if `qop` is "auth" or "auth-int" then
`response` = MD5(`HA1`:nonce:nonceCount:cnonce:qop:`HA2`)
else if `qop` is unspecified then
`response` = MD5(`HA1`:nonce:`HA2`)
计算练习
假定参数:
username
: Mufasapassword
: Circle Of Lifecnonce
: 0a4f113bnonceCount
: 00000001
已知server发来要求验证身份的报文相关信息为:
WWW-Authenticate: Digest realm="testrealm@host.com",
qop="auth,auth-int",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
分析:
algorithm
unspecified- -> HA1 = MD5(username:realm:password)
qop
auth,auth-int(可认为只取auth)- -> HA2=MD5(method:digestURI)
- -> response=MD5(HA1:nonce:nonceCount:cnonce:qop:HA2)
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
DIGEST认证提供了高于BASIC认证的安全等级,但是和HTTPS的客户端认证机制相比仍旧很弱。DIGEST认证提供防止密码被窃听的保护机制,但并不存在防止用户伪装的保护机制。
FormBase认证
表单认证并没有一个统一的标准,但一般会用Cookie
来管理Session
(Cookie
字段用于弥补HTTP的无状态性)
步骤
Session管理
如果涉及到Session操作的,服务器则会返回Set-Cookie
字段,这个字段中包含SessionID
的信息,用来唯一标识一个session
比如Tomcat
返回的字段就是Set-Cookie: JSESSIONID=D43BA0A1EC5F9681B1BADCE67A5AA5C6
Session是如何运作的?
在Tomcat容器中,由ManagerBase在内存中维护了一张<String, Session>
表,通过提取请求中的JESSIONSID
信息,来查找到当前的session的信息
请求中的JESSIONSID
来自:
- URI Parameters
- Cookies
- SSL SessionID
Session什么时候创建?
在Tomcat容器中,只有当前session不存在时,且调用了getSession
才会通过Manager创建,即仅访问静态资源是不会创建的。
而在默认情况下,JSP文件会调用getSession
来跟踪会话,所以在这些JSP页面中,Cookie都会带有JSESSIONID
字段
保护SessionID的常用手段?
不难看出,一旦泄露了SessionID,对方就可以伪装成你的身份恶意操作了。
通常一种安全的保存方法是,先利用给密码加盐(salt)的方式增加额外信息,再使用散列(hash)函数计算出散列函数后保存。
根据上文的Digest认证的思想,可以通过digest=md5(Salt+SessionID)
的方式来加密传递的SessionID
- Salt可以是时间戳等动态参数
- 将
Salt
和digest
一起发送,来避免重放攻击
参考
- 上野宣-《图解HTTP》
- Wikipedia-Digest access authentication
- Tomcat Embed Core 8.0.32