一文了解基于 HTTP 的认证模式

1,970 阅读14分钟

现在HTTP的应用非常普遍,并且很多都会有自己认证和授权机制,用于保护应用系统中的资源和敏感信息,下面就对常用的HTTP认证授权方式总结一下

通用的HTTP认证框架

RFC7235中定义了一个HTTP身份认证框架,用于限制用户访问HTTP应用系统的权限;在这套框架中,服务器可以向客户端发送的请求发送Challenge(质询信息),客户端则可提供身份认证凭证(Credential)

质询与应答的工作流程图如下所示:

1665235256851.png

该认证模式整个过程的步骤如下所示:

  1. 客户端发送请求到服务端

  2. 服务端向客户端返回401Unauthorized,未授权的)状态码,并在www-Authenticate首部提供如何进行认证的信息,至少包含一种质询方式;首部中的www-Authentiate字段值格式如下:

     WWW-Authenticate: <type> [realm=<realm>] [charset=<charset>]
    
    • <tpye>:认证类型,比如:Basic
    • realm:一个保护区域的描述,如果没有指定,客户端通常会显示一个格式化的主机名代替
    • chartset:告知客户端用户名和密码首选的编码方式,现在基本都是utf-8

    下面是一个例子:

     WWW-Authenticate: Basic realm="Access to the staging site"
    
  3. 客户端在新的请求中添加Authorization首部字段进行认证,字段值为身份凭证信息;Authorization的格式如下:

     Authorization: <type> <credentials>
    
    • type:认证类型,Authorization头部中设置的值将与WWW-Authenticate返回的类型保持一致,如:Basic
    • credentials:用户的身份凭证

    下面是一个例子:

     Authorization: Basic dXNlcjpwYXNz
    
  4. 服务端验证身份,验证成功返回:200 OK,验证失败返回:401 Unauthorized

代理认证

代理认证的原理与上面类似,不同的在于:

  1. 代理认证质询(challenge)的状态码为:407,并且必须提供代理证书
  2. 响应头为Proxy-Authenticate而不是www-Authenticate,值和www-Authenticate是一样的
  3. 请求头为Proxy-Authorization而不是Authorization,值和Authorization字段的是一样的

基于 HTTP 认证框架的几种认证方案

HTTP协议中提供了一些具体的认证方案用来控制用户对特定站点和应用的访问,如下是几种基于 HTTP 认证框架的具体方案:

  • Basic AuthBase64编码认证
  • Digest Auth:摘要认证
  • Hawk Auth:消息鉴权码
  • NTLM Auth:基于挑战/应答的身份验证协议

不同的认证方式在安全强度以及在客户端或服务器端软件中可获得的难易程度不同,在同一个应用当中也有可能存在多种不同的认证方式,下面就对这几种认证方式进行详细的说明

在介绍具体的认证方案之前,首先来接受一下在 HTTP 认证方案中常用,但是比较陌生的一个字段:realm

认证域(Realm)

认证域(又称防护域,protection space)表示服务器上一个资源组;一个服务器可能会拥有多个认证域,认证域的存在意味着用户访问不同的资源组所需的认证信息不同;比如:在一个服务器上有统计网站信息的认证域,并且只允许管理员访问,则Realm有如下的表示:

 资源:/admin/statistic/financials.txt
 对应的认证域:Realm="Admin Statistics"

或者是存放图片的认证域:

 资源:/images/img1.png
 Realm=“Images”

当普通用户访问网站统计信息的时候,服务器就会响应质询信息:

 HTTP/1.0 401 Unauthorized
 WWW-Authenticate: Basic realm="Admin Statistics"

当然,认证域是HTTP协议提供的一种区分不同资源组的方式,但是如果服务端没有指定的话,那么这个Realm默认就是当前服务器的主机

认证域的存在可以让服务端将其受保护的资源进行划分,每一种类型的资源单独生成一个保护区(protect space)并且对应不同的认证信息,这样就可以实现服务端的资源隔离了

Basic Auth

Basic Auth是最简单且常用的认证协议,从HTTP/1.0开始就引入了,规范文档记录在rfc2671中;在Basic Auth中,客户端请求指定认证域(Realm)的资源时,需要通过username,password来认证自己的身份,认证通过才能访问该域的资源

简单的模仿一下客户端请求指定认证域的过程;首先,客户端发送请求,如下:

 GET /images/img.jpg HTTP/1.1
 Host: www.exmplae.com

服务器向客户端发出质询(challenge)信息,如下所示:

 HTTP/1.1 401 Access Denied
 WWW-Authenticate: Basic realm="images"

此处的WWW-Authenticate: Basic realm="images"就是告诉客户端,访问该域(realm)的资源使用的认证方式为Basic Auth,客户端收到响应之后,提示用户输入认证信息,再次发送请求到服务器:

 GET /images/img.jpg HTTP/1.1
 Authorization: Basic YWRtaW46MTIzNDU2

其中Authorization: Basic YWRtaW46MTIzNDU2表示认证方式为Basic Auth,认证信息为:YWRtaW46MTIzNDU2;这部分认证信息是格式为:base64(username:password)生成,上面的认证信息中username=admin,password=123456

服务端收到认证信息之后进行进行校验,校验成功就会返回对应的图片信息,如下所示:

 HTTP/1.1 200 OK
 Content-type: image/jpeg
 ...<image data>

Basic Auth因为在HTTP中是明文传输,所以这种认证方式不是一种安全的认证方式,除非和其他外部的安全一系统结合使用,比如,使用SSL/TLS将传输数据加密处理

Digest Auth

摘要认证也是在HTTP1.0规范中引入的认证模式,它是比Basic Auth更安全的一种认证模式,引入摘要认证的目的仅仅是为了弥补Basic Auth明文传输账密的缺点,并不能作为Web认证的终极解决方案,因为它对于传输的内容并没有加密

认证过程

摘要认证和Basic Auth认证类似,都是基于质询-响应(challenge-response)模式;下面是一次摘要认证的流程:

  1. 客户端发送请求获取资源

     GET /images/img.jpg HTTP/1.1
     Host: www.exmplae.com
    
  2. 服务端收到请求之后,发现没有认证信息,则发送质询信息

     HTTP/1.1 401 Unauthorized
     WWW-Authenticate:Digest realm="images",
                             qop="auth,auth-int",
                             nonce="cmFuZG9tbnVtYmVyb2ZkaWdlc3RhdXRo",
                             opaque="dsadac403ebaf9f0171e9517f40e41",
                             algorithm=MD5
    

    WWW-Authenticate中指定了认证方式为Digest,后面其他字段的含义如下:

    • realm:为请求的认证域,指获取该域下面的资源所需要的认证方式
    • qop:表示保护的质量(quantity of protection),包含auth,auth-int两种策略,其中auth-int增加了报文的完整性检测 (注:此处只是对报文进行了完整性检测,但并没有加密)
    • nonce:服务端向客户端发送质询时带的一个随机数,该随机数一般会使用Base64编码,并且在计算请求摘要(request digest)是附加上去,从而使同一用户的密码多次请求生成不同的摘要,防止重放攻击
    • opaque:是由服务端生成的一个随机字串,使用Base64编码,之后客户端在对同一认证域或认证域的子域的请求中原样返回到服务端即可
    • algorithm:指定生成摘要的算法
  3. 客户端收到质询之后,向服务端发送认证信息

     GET /images/img.jpg HTTP/1.1
     Host: www.exmplae.com
     Authorization:Digest realm=”images“,username="admin",
                           nonce="cmFuZG9tbnVtYmVyb2ZkaWdlc3RhdXRo",
                           uri="/images/img.jpg",
                           algorithm=MD5,
                           qop=auth,
                           nc=00000001,
                           cnonce="0a4f113b",
                           response="5a1c3bb349cf6986abf985257d968d86",
                           opaque="dsadac403ebaf9f0171e9517f40e41"
    

    在请求头中的Authorization字段中,会添加此次请求的认证信息,该信息的第一个值Digest表示认证方式是摘要认证,后面的键值对的含义如下:

    • realm:为请求的认证域,指获取该域下面的资源所需要的认证方式

    • username:用户的账号

    • nonce:返回之前服务端发送给客户端的那个随机数

    • uri:此次请求的uri,表示要获取的资源的具体位置

    • algorithm:请求中生成摘要的算法

    • qop:保护质量的策略

    • nc:表示对服务端随机数的计次(nonce count),比如第一次请求时服务端响应了一个随机数,那么客户端再请求时该值就为nc=00000001,它的目的就是服务端探测该请求是否是重放请求,因为如果客户端的两次请求发送的该值是相同的,就可以认为是重放请求

      注:重放请求,也是回放请求,指客户端发送两次一模一样的请求

    • cnonce:客户端生成的随机数,并且在客户端和服务端都会使用,为了防止明文攻击提供的相互认证,并且可以保护消息的完整性

    • opaque:是之前服务端发送质询是发送给客户端的值,客户端只需要原封不同的返回即可

    • response:它是由客户端根据一些参数计算生成的摘要,发送给服务器之后,服务也按照相同的方式计算一遍,如果两者相同,说明请求是有效的,这样就可以避免明文传输密码了,具体的计算过程如下:

       HA1=MD5(A1)=MD5(username:realm:password)
      

      如果 qop = "auth" 或未指定,那么:

       HA2=MD5(A2)=MD5(RequestMethod:digestUri)
      

      requestMethod就是请求方法,比如:Get;digestUri就是上面的uri;如果 qop = "auth-in",那么:

       HA2=MD5(A2)=MD5(requestMethod:digestUri:MD5(entityBody))
      

      entityBody就是消息体;如果 qop="auth""auth-in",那么 response 计算如下:

       response=MD5(HA1:nonce:nc:cnonce:qop:HA2)
      

      如果 qop 未指定,那么 response 计算如下:

       response=MD5(HA1:nonce:HA2)
      
  4. 服务端收到请求之后,会自己计算哈希值并于从客户端收到的进行对比,若两者相同则认证成功,返回请求资源

     HTTP/1.1 200 OK
     ​
     Content-type: image/jpeg
     ...<image data>
    

摘要认证其实就是生成账密的摘要,因为该算法不可逆,所以可以用来保护密码,同时再增加一些随机数,防止重放攻击;整个认证过程相对于Basic Auth更加的复杂,也更加的安全

Hawk Auth(消息鉴权码)

Hawk是由前OAuth文档的编辑者Eran Hammer设计的一种新的HTTP认证模式,好像并没有收录到rfc中;该认证模式主要的目的如下:

  • 为那些不愿或着无法为所有资源部署TLS的服务简化和改进HTTP身份认证
  • 保护用户凭证防止其泄露,比如:客户端使用动态配置决定发送请求到那个服务器的时候
  • 避免在TLS握手阶段由于客户端验证服务器身份失败将请求通过不安全通道发送到恶意服务器时,用户凭证的暴露

此外,Hawk支持使用一个为bewit的查询参数授权第三方应用访问个人资源

Hawk模式需要在服务端和客户端之间建立一个共享的对称密钥,这个共享密钥第一次安全建立是通过TLS或者其他客户端和服务器端都支持的共享机密信息的方式

具体认证过程

下面通过一个例子来详细描述Hawk的整个认证的过程:

  1. 未认证的客户端访问服务器上受保护的资源,发送的HTTP请求如下:

     GET /resource/1?b=1&a=2 HTTP/1.1
     Host: example.com:8000
    
  2. 服务端回应一个认证质询信息:

     HTTP/1.1 401 Unauthorized
     WWW-Authenticate: Hawk
    

    其中WWW-Authenticate表明服务器仅支持Hawk的认证方式

  3. 客户端之前在访问该服务器资源时,已经获得了一套Hawk凭证集合,在服务端发送给客户端的凭证中包含如下信息:

    • Key identifier: dh37fgj492je
    • Key: werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn
    • Algorithm: hmac sha256
    • Hash: 6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=
  4. 客户端通过计算时间戳获取到一个随机数,然后再用之前服务端发送给客户端的信息HMACkey生成一个字串,最后将该字串进行Base64编码生成Request Mac,如下

     6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=
    
  5. 客户端使用如上的信息构造Http请求进行认证:

     GET /resource/1?b=1&a=2 HTTP/1.1
     Host: example.com:8000
     Authorization: Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", ext="some-app-ext-data", mac="6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE="
    
  6. 服务端收到请求之后,根据收到认证信息再次计算request mac与客户端传过来的进行对比,如果相同表示认证成功,并返回客户端请求的资源

    注:此处还有一点就是服务端还需要校验认证头中的ts参数,防止回放攻击(replay attack

总结

Hawk认证的整个过程和摘要认证的比较类似,它主要的作用就是对那些没使用TLS的服务进行保护,防止请求中的账密泄露

但是在Hawk认证中会使用一个时间戳来保持回放攻击,这样很容易会被恶意用户通过调整客户端的时钟来制造回放攻击;在Hawk中提供了一种方法,即使用NTP来同步客户端和服务端的时钟,这就限制了客户端需要支持NTP来保证时钟同步

最后在Hawk认证中也支持对消息体的验证,具体可以在这里;但是Hawk并没有对消息体加密,所以也有可能造成信息的泄露

NTLM认证

NTLM就是在1993年由windows NT3.1所引入的一种Lan Manager(局域网管理器);它主要是用于windows系统的登录认证的,当然也会在一些应用系统中使用

NTLM工作原理

NTLM的认证过程和上面的HTTP认证过程类似都是采用了质询-应答(challenge-response)的方式,下面是一次认证过程的详细描述:

  1. 客户端发送请求到服务器获取资源
  2. 服务器响应质询信息(challenge)和一个随机数
  3. 客户端用密码生成的哈希值作为密钥对该随机数进行加密,然后发送到服务端
  4. 服务端收到加密后的数据后,取出存储的该用户的密码哈希值,也要相同的算法加密之前的质询时的随机数
  5. 如果两者相互匹配,那么就认证成功;否则就拒绝访问

整个过程如下图所示:

1664779118426.png

NTLM安全性

NTLM是一种不安全的认证方式;主要的原因有如下几个:

  1. NTLM中密码哈希值是认证的关键信息,但是在系统中获取密码并不是很难;在客户端,密码是存储在SYSTEMSAM文件中,它可以被任何管理员级别的用户获取,并且也很容易收到黑客攻击;此外,在默认情况下,密码是存储在内存中的,因此可以使用像Mimikatz这样的工具提取到
  2. 在服务端,密码是存储在每个域控制器(domain controller)的NTDS.dit文件中,在那里,哈希很容易受到 DCSync 攻击,这会欺骗 DC 将其哈希存储与伪装成另一个 DC 的恶意软件同步
  3. 此外NTLM也很容易被暴力破解,因为它所使用的哈希算法是MD4MD4是在1991年创建的,它是很弱的一种哈希算法;并且所生成的密码哈希值也没有加盐(加盐就是在计算密码的哈希之前添加一段随机数,这样即使相同的密码生成的哈希值也不相同),所以很容易被攻击者暴力破解

windows 2000中,微软已经用Kerberos替换掉了NTLMKerberos是一种更加安全的认证协议,它依赖于票据授权服务(ticket graning service)或者密钥分发中心,并且使用的是加密的方式而不是哈希值

总结

NTLM是微软在window nt中发明的一种登录认证方式,虽然迭代了好几个版本,但是整个过程很容易遭受攻击,所以微软也在win 2000中放弃了NTLM转而使用Kerberos

同时NTLM所使用的哈希算法也已经过时了,即便之后更新版本之后的MD5,目前大部分的hash算法都开始使用sha 256了,包括在后面更新的摘要认证中也替换了MD5算法

参考链接:

developer.mozilla.org/en-US/docs/…

github.com/mozilla/haw…

www.rfc-editor.org/rfc/rfc7235…

www.rfc-editor.org/rfc/rfc2617…

blog.quest.com/ntlm-authen…