前几天,同事一个需求涉及到RFC2617的认证(authentication)【链接】。
为什么要采用HTTP认证呢?答案很简单:维护web应用的安全。(当时刚出来的时候挺安全的,但20多年过去了,道高一尺魔高一丈,攻击方法层出不穷)。
虽然如此,但既然要求使用HTTP认证,那就正好学习一下!
首先来介绍一下HTTP相关的概念,网上有些文章写得挺通俗易懂的,我就直接引用了:
HTTP 的认证机制
http协议支持两种认证方式:基本认证和摘要认证。
什么是认证
认证就是要给出身份证明,证明你就是你声称的那个人。
例如王二狗和牛翠花两个人在网上都互动半年了还没有见面,于是二狗向翠花发出了诚挚的邀请:翠花,五一来天津玩吧,我请你吃麻辣烫!本来二狗想先视频一下到时候好认人,但翠花说那样就没有惊喜感了。于是两人就采取了最原始的认证方式:对暗号,到时候翠花喊:天王盖地虎。二狗就喊:翠花好漂亮。
HTTP 基本认证(BASIC)
大体流程就类似于牛翠花和王二狗接头的过程。 1.翠花:走到一个人面前说,二狗带我去吃麻辣烫吧。 2.二狗:请说出你的暗号。 3. 翠花:天王盖地虎。 4.二狗:张亮麻辣烫走起。。。
映射到编程领域为:
- 客户端(例如Web浏览器):服务器,请把/family/son.jpg 图片传给我。
GET /family/son.jpg HTTP/1.1
2. 服务器:客户端你好,这个资源在安全区family里,是受限资源,需要基本认证,请带上你的用户名和密码再来HTTP/1.1 401 Authorization Required www-Authenticate: Basic realm= "family"
服务器会返回401,告知客户端这个资源需要使用基本认证的方式访问,我们可以看到在www-Authenticate
这个Header里面 有两个值,Basic:说明需要基本认证,realm:说明客户端需要输入这个安全区的用户名和密码,而不是其他区的。因为服务器可以为不同的安全区设置不同的用户名和密码。如果服务器只有一个安全区,那么所有的基本认证用户名和密码都是一样的。 3. 客户端: 服务器,我已经按照你的要求,携带了相应的用户名和密码信息了,你看一下
如果客户端是浏览器,那么此时就会弹出一个弹窗,让用户输入用户名和密码。
Basic 内容为: 用户名:密码 后的base64 内容.假设我的用户名为Shusheng007,密码为ss007 那么我的Basic的内容为 Shusheng007:ss007 对应的base64 编码内容U2h1c2hlbmcwMDcldUZGMUFzczAwNw==,如下所示GET /family/son.jpg HTTP/1.1 Authorization: Basic U2h1c2hlbmcwMDcldUZGMUFzczAwNw==
4. 服务器:客户端你好,我已经校验了你的用户名和密码,是正确的,这是你要的资源。HTTP/1.1 200 OK Content-type: image/jpg ...
至此这个HTTP事务就结束了,非常简单的一个认证机制,不过缺点也是蛮多的(比如密码直接以明文方式在网络上传输,恶意用户一旦嗅探到,便等于控制了你的账户)。
HTTP 摘要认证(DIGEST)
DIGEST 认证的步骤:为弥补 BASIC 认证存在的弱点,从 HTTP/1.1 起就有了 DIGEST 认证。 DIGEST 认证同样使用质询 / 响应的方式(challenge/response),但不会像 BASIC 认证那样直接发送明文密码。所谓质询响应方式是指,一开始一方会先发送认证要求给另一方,接着使用从另一方那接收到的质询码计算生成响应码。最后将响应码返回给对方进行认证的方式。
步骤 1: 请求需认证的资源时,服务器会随着状态码 401Authorization Required,返回带WWW-Authenticate 首部字段的响应。该字段内包含质问响应方式认证所需的临时质询码(随机数,nonce)。首部字段 WWW-Authenticate 内必须包含realm 和nonce 这两个字段的信息。客户端就是依靠向服务器回送这两个值进行认证的。nonce 是一种每次随返回的 401 响应生成的任意随机字符串。该字符串通常推荐由Base64 编码的十六进制数的组成形式,但实际内容依赖服务器的具体实现。
步骤 2:接收到401状态码的客户端,返回的响应中包含 DIGEST 认证必须的首部字段 Authorization 信息。首部字段 Authorization 内必须包含 username、realm、nonce、uri 和response的字段信息。其中,realm 和 nonce 就是之前从服务器接收到的响应中的字段。
username是realm 限定范围内可进行认证的用户名。uri(digest-uri)即Request-URI的值,但考虑到经代理转发后Request-URI的值可能被修改因此事先会复制一份副本保存在 uri内。
response 也可叫做 Request-Digest,存放经过 MD5 运算后的密码字符串,形成响应码。
步骤 3:接收到包含首部字段 Authorization 请求的服务器,会确认认证信息的正确性。认证通过后则返回包含 Request-URI 资源的响应。并且这时会在首部字段 Authentication-Info 写入一些认证成功的相关信息。(不过我下面的例子没有去写这个Authentication-Info,而是直接返回的数据。因为我实在session里缓存的认证结果)。
Digest 认证涉及到的参数的含义
WWW-Authentication:用来定义使用何种方式(Basic、Digest、Bearer等)去进行认证以获取受保护的资源
realm:表示Web服务器中受保护文档的安全域(比如公司财务信息域和公司员工信息域),用来指示需要哪个域的用户名和密码
qop:保护质量,包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略,(可以为空,但是)不推荐为空值
nonce:服务端向客户端发送质询时附带的一个随机数,这个数会经常发生变化。客户端计算密码摘要时将其附加上去,使得多次生成同一用户的密码摘要各不相同,用来防止重放攻击
nc:nonce计数器,是一个16进制的数值,表示同一nonce下客户端发送出请求的数量。例如,在响应的第一个请求中,客户端将发送“nc=00000001”。这个指示值的目的是让服务器保持这个计数器的一个副本,以便检测重复的请求
cnonce:客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护
response:这是由用户代理软件计算出的一个字符串,以证明用户知道口令
Authorization-Info:用于返回一些与授权会话相关的附加信息
nextnonce:下一个服务端随机数,使客户端可以预先发送正确的摘要
rspauth:响应摘要,用于客户端对服务端进行认证
stale:当密码摘要使用的随机数过期时,服务器可以返回一个附带有新随机数的401响应,并指定stale=true,表示服务器在告知客户端用新的随机数来重试,而不再要求用户重新输入用户名和密码了
参考: