> For the complete documentation index, see [llms.txt](https://anjia1.gitbook.io/cs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://anjia1.gitbook.io/cs/http/header/authentication.md).

# HTTP Authentication

身份认证、身份验证

HTTP 提供了多种身份认证方案，它们通过 challenge–response（质询响应）让服务器在提供所请求的内容之前识别并发出质询。

* Basic Access Authentication，基本访问认证（验证）
* Digest Access Authentication，摘要访问认证（验证）

## 一. 通用框架

HTTP 为访问控制和身份认证提供了一个通用框架，通过一组可扩展的 challenge–response 身份认证方案。使用此方案，服务器可以 challenge 客户端请求，客户端可以提供 authentication 信息。

1. 服务器在收到 unauthenticated（未经身份认证）的 request 后，如果希望浏览器向其认证自身身份，那么服务器返回的 response 必须包含以下两部分：
   * `HTTP 401 Unauthorized` 的状态行
   * `WWW-Authenticate` header
2. 当浏览器要向服务器发送 authentication credential（身份认证凭证）时，使用 `Authorization` header。

<table><thead><tr><th width="440">Response</th><th>Request</th></tr></thead><tbody><tr><td><code>HTTP 401 Unauthorized</code></td><td></td></tr><tr><td><code>WWW-Authenticate: &#x3C;method> x=x, y=y, ...</code></td><td><code>Authorization: &#x3C;method> xxx</code></td></tr></tbody></table>

> 单词 authenticate，证明...是真实的、证实，prove sth is real or true
>
> 单词 authorization，批准、授权，official permission
>
> 所以是：
>
> * server 要求 client 证明自己的身份
> * client 提供自己的身份信息，请求 server 授权

HTTP Authentication 规范还允许服务器在根 URI 下定义单独的 authentication scope，即 Authentication Realm（认证领域）。

## 二. 基本访问认证

基本访问认证是对 web 资源进行访问控制的一种最简单的技术，因为它不需要 cookie、session ID 以及登录页面，而是使用的 HTTP header 中的标准字段。HTTP 基本认证最初是在 1993 年实现的，1996 年在 HTTP 1.0 规范中定义，2015 年在 RFC 7617 中指定。

基本访问认证提供了一种在 HTTP request 中设置用户名和密码的方法。

在 HTTP 基本认证中，request 中包含一个 `Authorization` header 字段，形如：

```http
Authorization: Basic <credentials>
```

其中 `<credentials>` 的值是 `username:password` 的 Base64 编码。

### `WWW-Authenticate`

```http
WWW-Authenticate: Basic realm="User Visible Realm"
WWW-Authenticate: Basic realm="User Visible Realm", charset="UTF-8" 
# 若服务器希望客户端使用 UTF-8 来编码 username 和 password
```

### `Authorization`

`Authorization` header 的值，构造步骤如下：

1. 用户名和密码用 `:` 分隔。这意味着用户名本身不能包含 `:`&#x20;
2. 将字符串编码为八位位组序列。默认情况，只要它和 US-ASCII 兼容就行。服务器也可以通过 `charset` 参数指定建议的编码方式（是在 Response 的 `WWW-Authenticate` header 中）
3. 用 Base64 进行编码
4. 将 authorization method（如 `Basic`）和一个空格字符（即 `Basic` ）拼接在最前面

```http
# window.btoa('anjia:password')
Authorization: Basic YW5qaWE6cGFzc3dvcmQ=
```

### 使用说明

1. 为了保证机密性，HTTP 基本认证通常会和 HTTPS 配合使用，毕竟它只用了 Base64 编码
2. 通常需要浏览器缓存凭据（credential）一段合理的时间，以避免不断提示用户输入用户名和密码，毕竟它要求每个 HTTP 请求的 header 中必须提供 `Authorization` 字段。而浏览器之间的缓存策略可能会不相同
3. HTTP 不提供 web 服务器指示客户端退出登录的方法。但是，有多种方法可以清除某些 web 浏览器中缓存的凭据。其中之一是故意让凭据不正确，从而把用户重定向到同域中的 URL。然而，这种行为在不同的浏览器和浏览器版本之间是不一致的。比如 Microsoft IE 提供了专用的 JavaScript 方法来清除缓存的凭据 `document.execCommand('ClearAuthenticationCache')`
4. 在现代浏览器中，用于基本认证的缓存凭据，通常会在清除浏览历史记录时被清除。大多数浏览器允许用户单独清除凭据，尽管那个选项可能很难找，并且通常会清除所有访问过的站点的凭据
5. 浏览器不会主动检测和阻止暴力破解凭据，除非使用服务器端机制

> 补充：
>
> 还有一种被称为 Bearer 认证的方式，它将一个 token 存放在 `Authorization` header 中用于身份认证，这个 token 可以是一个简单的随机串，也可以是一个包含更多信息的 JWT（JSON Web Token）字符串，这依赖于 server 的使用方式。
>
> 在用户登录成功之后，server 端根据用户名和密码生成 token，并将其返回给前端，前端将 token 存储在 session storage 中。对于要进行身份认证的 API，前端在发送 HTTP request 之前，取出 token 并按规则拼好，即 `<authorization method> + 空格 + token`，将值赋给 `Authorization` header。
>
> 此时的身份认证相关的信息都在 server 端维护，包括加密、缓存时间。前端要做的就是处理好相应的 response code，即当返回 `HTTP 401 Unauthorized` 时，跳转登录页面。 &#x20;

## 三. 摘要访问认证

摘要访问认证是 web 服务器和浏览器协商 credential（凭据，比如用户名、密码）的方法之一，用于在发送敏感信息之前确认用户身份。该机制会对用户名和密码应用哈希函数，在通过网络发送它们之前。

从技术上讲，摘要认证是 MD5 cryptographic hashing（MD5 加密哈希）的一个应用，同时使用 nonce（随机数）值来防止重放攻击（replay attack）。摘要认证使用 HTTP 协议。

> 在 cryptography（密码学）中，nonce（随机数）是在密码通信中只能使用一次的任意数字。
>
> nonce 通常是在认证协议中发出的随机数或伪随机数，以确保旧通信不能在重放攻击中重复使用。
>
> nonce 还可以用作初始化向量和加密哈希函数。

### 安全性

在 RFC 2617 中，安全性 = MD5 Hash + 可选的增强功能

<table><thead><tr><th width="164.33333333333331">安全性</th><th width="266">说明</th><th>谁负责</th></tr></thead><tbody><tr><td>MD5 Hash</td><td>哈希函数、加密算法</td><td>server 和 client 都会用到</td></tr><tr><td>qop</td><td>quality of protection，保护质量</td><td>server 提供</td></tr><tr><td>nonce</td><td>随机数</td><td>server 和 client 会各自生成自己的</td></tr><tr><td>nonce counter</td><td>随机数计数器</td><td>client 提供（依次递增）</td></tr></tbody></table>

2015 年 9 月，RFC 7616 取代了 RFC 2617，添加了 4 个新算法：`SHA-256`, `SHA-256-sess`, `SHA-512-256` 和 `SHA-512-256-sess`。该编码相当于 `MD5` 和 `MD5-sess` 算法，只是将 MD5 hash 函数替换成了 SHA-256 和 SHA-512-256 函数。只是浏览器对新算法的支持还不太好。

关于 MD5 安全性对摘要认证的影响，简要介绍如下：

* HTTP 摘要认证中使用的 MD5 计算旨在单向，这意味着当仅知道输出时，应该很难确定原始输入。但如果密码本身太简单，那么可以测试所有可能的输入并找到匹配的输出（暴力攻击），也许可以借助字典或合适的查找列表，这对 MD5 来说可是很容易可行的。
* HTTP 方案是由 Phillip Hallam-Baker 在 CERN 于 1993 年设计的，并没有纳入认证系统的后续改进，比如 HMAC（keyed-hash message authentication code，密钥哈希消息认证代码）的开发。
* 加密结构是基于 MD5 散列函数，到目前为止，MD5 冲突攻击尚未被证明对摘要式认证构成威胁，RFC 2617 允许服务器实现检测某些冲突和重放攻击的机制。

### `WWW-Authenticate`

```http
HTTP/1.0 401 Unauthorized
WWW-Authenticate: Digest realm="testrealm@host.com",
                        qop="auth,auth-int",
                        nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                        opaque="5ccc069c403ebaf9f0171e9517f40e41"
```

参数说明：

1. authentication `realm`：通常是对正在访问的计算机或系统的描述
2. `nonce`：response 随机数，是随机生成的一次性值
3. `qop`：quality of protection，保护质量

### `Authorization`

提供了用户名和密码之后，客户端会重新发送相同的请求，并会把 response 里包含的 authentication headers 都加上。

```http
GET /dir/index.html HTTP/1.0
Host: localhost
Authorization: Digest username="Mufasa",
                     uri="/dir/index.html",
                     nc=00000001,
                     cnonce="0a4f113b",
                     response="6629fae49393a05397450978507c4ef1",
                     realm="testrealm@host.com",
                     qop=auth,
                     nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                     opaque="5ccc069c403ebaf9f0171e9517f40e41"
```

新参数的说明：

1. `username`：
2. `uri`：
3. `nc`：nonce counter，客户端递增的随机数计数器
4. `cnonce`：client nonce，nonce
5. `response`：其值是根据指定的 algorithm 和 qop，利用加密函数 `MD5()` 算出来的
   * 由于服务器和客户端具有相同的信息，因此可以通过执行相同的计算来检查 response

```js
HA1 = MD5(username:realm:password)                    // algorithm = 'MD5' 或未指定
HA1 = MD5(MD5(username:realm:password):nonce:cnonce)  // algorithm = 'MD5-sess'

HA2 = MD5(method:digestURI)                          // qop = 'auth' 或未指定
HA2 = MD5(method:digestURI:MD5(entityBody))          // qop = 'auth-int' 

response = MD5(HA1:nonce:nonceCount:cnonce:qop:HA2)  // qop = 'auth' 或 'auth-int'
response = MD5(HA1:nonce:HA2)                        // qop 未指定
```

举个例子：其中 `MD5()` 表示一个用来计算 MD5 hash 的函数，`\` 表示换行连续。

```js
/**
 * HA1 = MD5(username:realm:password) 用该公式，因为未指定 algorithm
 *     username 的值是 "Mufasa"
 *     realm 的值是 "testrealm@host.com"
 *     password 的值是 "Circle Of Life"
 */
HA1 = MD5( "Mufasa:testrealm@host.com:Circle Of Life" )
    = 939e7578ed9e3c518a452acee763bce9
  
/**
 * HA2 = MD5(method:digestURI) 用该公式，因为 qop 的值是 'auth'
 *     method 的值是 "GET"
 *     digest URI 的值是 "/dir/index.html"
 */
HA2 = MD5( "GET:/dir/index.html" )
    = 39aff3a2bab6126f332b942af96d3366

/**
 * response = MD5(HA1:nonce:nonceCount:cnonce:qop:HA2) 用该公式，因为 qop 的值是 'auth'
 *     nonce 即 server nonce，值是
 *     nonceCount，即 request counter (nc)，值是
 *     cnonce，即 client nonce，值是
 *     qop 的值是
 */
Response = MD5( "939e7578ed9e3c518a452acee763bce9:\
                    dcd98b7102dd2f0e8b11d0f600bfb0c093:\
                    00000001:0a4f113b:auth:\
                    39aff3a2bab6126f332b942af96d3366" )
        = 6629fae49393a05397450978507c4ef1
```

此时，客户端可能会发出另一个 request，重用服务器的 nonce（随机数）值（服务器只会为每个 `401` response 发新的 nonce），同时提供新的客户端随机数（cnonce）。对于后续请求，十六进制 request counter（nc，请求计数器）必须大于其使用的最后一个值，否则攻击者就可以简单地使用相同的 credentials 重放旧 request。服务器负责确保 counter（计数器）针对其发出的每个 nonce 值而递增，从而适当地拒绝任何错误请求。显然，更改 method、URI 或 counter 的值都将导致不同的 response value。

服务器应该记住它最近生成的 nonce 值。它还可以记住每个 nonce 值的发布时间，并在一定时间后使其过期失效。如果使用过期值，服务器应使用 `401` 状态代码进行响应，同时添加 `stale=TRUE` 到 authentication header，以指示客户端应使用提供的新 nonce 重新发送，而不是提示用户输入另一个用户名和密码。

服务器不需要保留任何过期的 nonce 值，它可以简单地假设任何无法识别的值都是已经过期的。服务器也可以允许每个 nonce 只能返回一次，尽管这会迫使客户端重复每个请求。请注意，让服务器 nonce 立即过期是行不通的，因为客户端永远没有机会使用它。

其它：

在 server 端，`.htdigest` 文件用来存储摘要认证的 username、realm 和 password。

在 client 端，有些浏览器不支持某些功能，比如 auth-int checking, MD5-sess algorithm。如果服务器要求处理这些可选功能，客户端可能无法进行身份认证。

还有些软件已经弃用摘要认证了，因为它（与基本认证 over HTTPS 相比）的一些缺点，比如软件 Bitbucket、Symfony PHP framework。

### 使用说明

#### 优点

HTTP 摘要认证的一些安全优势，包括：

* 密码未明确发送到服务器
* 密码不直接在摘要中使用，而是在 `HA1 = MD5(username:realm:password)` 中，这允许某些实现存储 HA1 而不是明文密码
* RFC 2617 中引入了客户端随机数，允许客户端防止明文攻击
* 服务器随机数允许包含时间戳，因此服务器可以检查客户端提交的 nonce 属性，以防止重放攻击
* 服务器还可以维护最近发布或使用的服务器 nonce 值的列表，以防止重复使用
* 可以防止网络钓鱼（把真正的请求发到恶意服务器），因为 plain 密码永远不会发送到任何服务器，无论服务器是否正确（公钥系统依赖于用户能认证 URL 是否正确）

#### 缺点

HTTP 摘要认证的几个缺点：

* 网站无法控制最终向用户呈现的界面
* RFC 2617 中的许多安全选项都是可选的。如果服务器未指定保护质量（qop），则客户端将以安全性更低的旧版 RFC 2069 模式运行
* 摘要访问认证容易受到 MITM（中间人）攻击。例如，MITM 攻击者可以告诉客户端使用基本访问认证或传统 RFC2069 摘要访问认证模式。为了进一步扩展这一点，摘要访问认证不为客户端提供认证服务器身份的机制
* 服务器可以存储 `HA1 = MD5(username:realm:password)` 而不是密码本身。然而，如果存储的 HA1 被泄露，攻击者可以生成有效的响应并访问域中的文档，就像访问密码本身一样容易。因此，存 HA1 值的表必须像包含明文密码的文件一样受到安全保护
* 摘要访问认证阻止在存储密码时使用强密码哈希（比如 bcrypt），因为密码或摘要用户名、领域和密码必须是可恢复的
* 此外，由于 FIPS 中不允许使用 MD5 算法，因此 HTTP 摘要认证将无法与 FIPS 认证的加密模块一起使用

#### 替代身份认证协议&#x20;

到目前为止，最常见的方法是使用 HTTP+HTML form-based authentication（基于 HTTP+HTML 表单的身份认证）明文协议，或更罕见的方法是使用基本访问认证。 这些弱明文协议与 HTTPS 网络加密一起使用，可以解决摘要访问认证旨在防止的许多威胁。 然而，HTTPS 的这种使用依赖于终端用户能准确地认证他们每次访问的 URL 是正确的，以防止将其密码发送到不受信任的服务器，从而导致网络钓鱼攻击。 可惜的是，用户经常无法做到这一点，这就是为什么网络钓鱼已成为最常见的安全漏洞形式。

一些偶尔使用的基于 Web 的应用程序的强认证协议包括：

* 使用客户端证书进行公钥认证，通常通过 HTTPS/SSL 客户端证书实现
* Kerberos 或 SPNEGO 认证，比如由 Microsoft IIS 使用（运行配置为集成 Windows 认证）
* 安全远程密码协议，最好在 HTTPS/TLS 层内
* JSON Web Token 是基于 JSON 的标准 RFC 7519，用于创建断言一定数量声明的访问令牌

## 主要参考

1. [Wikipedia: Basic\_access\_authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
2. [Wikipedia: Digest\_access\_authentication](https://en.wikipedia.org/wiki/Digest_access_authentication)
3. [RFC 2617](https://datatracker.ietf.org/doc/html/rfc2617): HTTP Authentication: Basic and Digest Access Authentication
