HTTP 认证基础:Basic Auth 与 Digest Auth

HTTP 认证是 API 安全的第一道防线。在 OpenAPI 规范中,securitySchemes 字段最常引用的两种 HTTP 认证方案就是 Basic Auth 和 Digest Auth。理解它们的原理、实现方式和适用场景,是掌握 OpenAPI 权限验证的基石。

2.1 HTTP Basic Auth 原理与实现

2.1.1 工作原理

Basic Auth 是最简单的 HTTP 认证方式。客户端在请求头中直接发送 用户名:密码 的 Base64 编码值。服务器解码后验证凭据。

  • 认证头格式:Authorization: Basic <Base64(user:password)>
  • 无状态:每次请求都携带凭据,服务器无需维护会话。
  • 明文传输:Base64 是编码而非加密,中间人可轻易解码。
关键点:Basic Auth 必须配合 HTTPS 使用,否则密码等于明文传输。

2.1.2 OpenAPI 规范定义

components:
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
      description: "使用用户名和密码进行 Basic 认证"
security:
  - basicAuth: []

2.1.3 服务端实现示例(Node.js + Express)

const express = require('express');
const app = express();

app.use((req, res, next) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Basic ')) {
    res.set('WWW-Authenticate', 'Basic realm="API Access"');
    return res.status(401).send('Unauthorized');
  }

  const base64Credentials = authHeader.split(' ')[1];
  const credentials = Buffer.from(base64Credentials, 'base64').toString('ascii');
  const [username, password] = credentials.split(':');

  // 验证凭据(示例硬编码,生产应查数据库)
  if (username === 'admin' && password === 'secret123') {
    req.user = { username };
    next();
  } else {
    res.status(401).send('Invalid credentials');
  }
});

app.get('/api/protected', (req, res) => {
  res.json({ message: `Hello ${req.user.username}` });
});

app.listen(3000);

2.2 HTTP Digest Auth 原理与实现

2.2.1 工作原理

Digest Auth 解决了 Basic Auth 的密码明文问题。它使用 挑战-响应 机制,服务器发送随机数(nonce),客户端用密码和 nonce 计算哈希值,服务器验证哈希而非密码本身。

  • 认证流程:服务器返回 401 + WWW-Authenticate 头 → 客户端计算响应 → 服务器验证。
  • 哈希算法:默认使用 MD5,计算 HA1:nonce:HA2。
  • 防重放:nonce 每次不同,防止重放攻击。
提示:Digest Auth 虽然比 Basic Auth 安全,但仍易受中间人攻击(MITM),且 MD5 已不再安全。现代 API 更推荐 Bearer Token 或 OAuth 2.0。

2.2.2 OpenAPI 规范定义

components:
  securitySchemes:
    digestAuth:
      type: http
      scheme: digest
      description: "使用 Digest 挑战-响应认证"
security:
  - digestAuth: []

2.2.3 服务端实现示例(Node.js + Express)

const express = require('express');
const crypto = require('crypto');
const app = express();

const users = { 'admin': 'secret123' };
let nonceStore = {};

app.get('/api/protected', (req, res) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Digest ')) {
    const nonce = crypto.randomBytes(16).toString('hex');
    nonceStore[nonce] = Date.now();
    res.set('WWW-Authenticate', `Digest realm="API", nonce="${nonce}", qop="auth"`);
    return res.status(401).send('Unauthorized');
  }

  // 解析 Digest 参数
  const params = {};
  authHeader.replace(/Digest\s+/,'').split(', ').forEach(p => {
    const [k, v] = p.split('=');
    params[k] = v.replace(/"/g, '');
  });

  const { username, realm, nonce, uri, response, qop, nc, cnonce } = params;
  const password = users[username];
  if (!password) return res.status(401).send('Invalid user');

  // 计算期望的响应
  const HA1 = crypto.createHash('md5').update(`${username}:${realm}:${password}`).digest('hex');
  const HA2 = crypto.createHash('md5').update(`GET:${uri}`).digest('hex');
  const expectedResponse = crypto.createHash('md5')
    .update(`${HA1}:${nonce}:${nc}:${cnonce}:${qop}:${HA2}`)
    .digest('hex');

  if (response === expectedResponse) {
    res.json({ message: `Hello ${username}` });
  } else {
    res.status(401).send('Invalid credentials');
  }
});

app.listen(3000);

2.3 核心流程对比

下图展示了 Basic Auth 与 Digest Auth 的认证流程差异:

Basic Auth vs Digest Auth 认证流程对比 Basic Auth 客户端发送 Base64(user:password) 服务器解码并验证凭据 密码以 Base64 编码传输(可逆) Digest Auth 服务器返回 401 + nonce 客户端计算 hash(nonce+password) 服务器验证哈希值(密码不传输) 核心差异 Basic Auth 传输可逆编码的密码;Digest Auth 传输不可逆哈希,密码永不出现在网络中

2.4 优缺点分析

特性 Basic Auth Digest Auth
实现复杂度 极低,仅需 Base64 编解码 较高,需处理 nonce、qop、哈希计算
密码传输 Base64 编码(可逆) MD5 哈希(不可逆)
HTTPS 必要性 必须,否则密码完全暴露 推荐,虽不传明文但易受 MITM 攻击
重放攻击防护 无 有(通过 nonce + nc 计数器)
服务端存储 需存储明文或可逆密码 可存储 HA1 哈希值
性能 高,无额外计算 中,每次请求需哈希计算
浏览器支持 所有浏览器原生支持 所有浏览器原生支持
现代 API 适用性 仅适合内部工具或测试 较少使用,已被 Bearer Token 取代

2.5 安全建议与最佳实践

警告:Basic Auth 和 Digest Auth 在现代 API 安全中已属于过时方案。除非是遗留系统或极低安全要求的内部工具,否则应优先考虑 Bearer Token、OAuth 2.0 或 API Key。
  • Basic Auth 使用场景:快速原型、内部调试工具、与旧系统兼容。
  • Digest Auth 使用场景:无法使用 HTTPS 的遗留环境(极少见)。
  • OpenAPI 规范建议:在 securitySchemes 中明确标注认证类型,并在描述中说明安全要求。
  • 密码存储:服务端应使用 bcrypt 等强哈希算法存储密码,而非明文或 MD5。
  • 速率限制:对认证端点实施速率限制,防止暴力破解。

2.6 OpenAPI 中的组合使用

在 OpenAPI 3.x 中,可以同时定义多种 HTTP 认证方案,并在不同路径上应用不同的安全策略:

components:
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
    digestAuth:
      type: http
      scheme: digest

paths:
  /api/legacy:
    get:
      security:
        - basicAuth: []
      responses:
        '200':
          description: Legacy API 响应
  /api/secure:
    get:
      security:
        - digestAuth: []
      responses:
        '200':
          description: Secure API 响应
实践要点:在 OpenAPI 文档中,security 字段可以定义多个认证方案(OR 关系),也可以在一个方案中定义多个作用域(AND 关系)。对于 HTTP Basic/Digest,通常只使用单个方案。