> 技术文档 > HTTP Digest 认证:原理剖析与服务端实现详解_digest认证

HTTP Digest 认证:原理剖析与服务端实现详解_digest认证


HTTP Digest 认证:原理剖析与服务端实现详解

HTTP 协议中的 Digest 认证(摘要认证)是一种比 Basic 认证更安全的身份验证机制,其核心设计是避免密码明文传输,并通过动态随机数(Nonce)防范重放攻击。本文将结合标准协议流程与具体服务端代码实现,深入解析 Digest 认证的技术细节。

一、Digest 认证的核心原理与流程

1.1 为什么需要 Digest 认证?

Basic 认证直接通过 Base64 编码传输用户名和密码(如 username:password),虽然编码可逆但无加密,安全性极差。Digest 认证通过哈希算法(如 MD5)对密码进行处理,客户端仅传输密码的哈希值(而非明文),且每次请求使用动态随机数(Nonce),大幅提升了安全性。

1.2 标准协议流程(RFC 7616)

Digest 认证的核心是 “挑战 - 响应” 模式,完整流程可分为 4 步:

步骤 1:客户端首次请求受保护资源

客户端访问服务端的受保护路径(如示例中的 /sd),未携带认证信息。

步骤 2:服务端返回 401 挑战(Challenge)

服务端返回状态码 401 Unauthorized,并在 WWW-Authenticate 头部中携带以下关键参数:

  • realm:认证域(如 MyProtectedSD),用于提示用户认证的上下文(如 “我的受保护存储”)。
  • nonce:服务端生成的一次性随机数(如 a1b2c3...),用于防止重放攻击。
  • opaque:服务端生成的固定字符串(如 MD5 哈希值),客户端需原样返回。
  • qop:认证质量(Quality of Protection),常见值为 auth(仅验证请求)。
  • algorithm:哈希算法(默认 MD5)。

示例头部:

WWW-Authenticate: Digest realm=\"MyProtectedSD\", qop=\"auth\", nonce=\"a1b2c3...\", opaque=\"d4e5f6...\", algorithm=\"MD5\"
步骤 3:客户端构造认证响应(Response)

客户端收到挑战后,提示用户输入用户名和密码,计算并组装 Authorization 头部,包含以下参数:

  • username:用户输入的用户名。

  • realm:服务端返回的 realm(需与本地存储的密码关联)。

  • nonce:服务端返回的 nonce(原样使用)。

  • uri:请求的资源路径(如 /sd)。

  • response:核心哈希值,通过以下公式计算:

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

    其中:

    • HA1 = MD5(username:realm:password)(客户端预计算的用户密码哈希)。
    • HA2 = MD5(method:uri)(请求方法与资源路径的哈希,如 GET:/sd)。
    • nc:请求计数(Nonce Count,防止重放攻击的递增序号)。
    • cnonce:客户端生成的随机数(Client Nonce,增强随机性)。
步骤 4:服务端验证响应

服务端收到 Authorization 头部后,根据以下逻辑验证:

  1. 校验 realmnonce(是否有效且未过期)、opaque 是否匹配。
  2. 根据用户名获取存储的密码(或预计算的 HA1)。
  3. 重新计算 HA1HA2 和期望的 response
  4. 比较客户端提供的 response 与服务端计算的 response,一致则认证成功。

二、服务端实现逻辑:代码解析

本文提供的服务端代码基于 Python 的 http.server 模块,实现了 Digest 认证的核心逻辑。以下是关键模块的详细解析。

2.1 全局配置与状态管理

# --- 配置 ---SERVER_ADDRESS = \'0.0.0.0\' # 监听所有接口,方便测试SERVER_PORT = 8001REALM = \"MyProtectedSD\" # 保护域名,会显示在客户端的认证提示中# 存储用户名和密码(在实际应用中,密码应该哈希存储,这里为了演示方便直接存储)# 或者更好的方式是存储 HA1 = MD5(username:realm:password)# 例如: HA1 = hashlib.md5(f\"your_username_here:{REALM}:your_password_here\".encode()).hexdigest()USERS_PASSWORDS = { \"admin001\": \"my_password_random_generation\"}# 存储已发出的 nonce 及其创建时间,用于校验和防止重放# {nonce_value: creation_timestamp}# 实际应用中,nonce 应该有过期机制,并且用后即焚或严格校验 nc (nonce count)active_nonces = {}NONCE_LIFETIME_SECONDS = 300 # Nonce 有效期,例如 5 分钟# Opaque 值,服务器生成,客户端原样返回OPAQUE = hashlib.md5(os.urandom(16)).hexdigest()
  • 用户存储:示例中直接存储明文密码(实际生产环境应存储预计算的 HA1 = MD5(username:realm:password))。
  • nonce 管理active_nonces 字典记录 nonce 及其生成时间,用于后续过期校验。
  • opaque:服务端生成的固定字符串,防止客户端篡改挑战参数。

2.2 nonce 生成与过期校验

def generate_nonce(): \"\"\"生成唯一的 nonce 并记录创建时间\"\"\" nonce = hashlib.md5((os.urandom(16).hex() + str(time.time())).encode()).hexdigest() active_nonces[nonce] = time.time() # 存储 nonce 与时间戳 return nonce

nonce 是 Digest 认证的安全基石,其生成需满足:

  • 随机性:通过 os.urandom(16) 生成随机字节,结合时间戳确保唯一性。
  • 时效性active_nonces 记录生成时间,超过 NONCE_LIFETIME_SECONDS(5 分钟)后失效,防止重放攻击。

2.3 挑战响应(401 状态码)

def send_401_challenge(self, stale=False): \"\"\"发送 401 Unauthorized 响应和 WWW-Authenticate 挑战头\"\"\" self.send_response(401) self.send_header(\'Content-type\', \'text/plain\') nonce = generate_nonce() # 生成新 nonce auth_challenge = f\'Digest realm=\"{REALM}\", qop=\"auth\", nonce=\"{nonce}\", opaque=\"{OPAQUE}\", algorithm=\"MD5\"\' if stale: auth_challenge += \', stale=\"true\"\' # 标记旧 nonce 过期,提示客户端使用新 nonce self.send_header(\'WWW-Authenticate\', auth_challenge) self.end_headers() self.wfile.write(b\"Authentication required.\") print(f\"Sent 401 challenge with new nonce: {nonce}\")

当客户端未携带认证信息或认证失败时,服务端返回 401 状态码,并通过 WWW-Authenticate 头部发送挑战参数。若 stale=True(如 nonce 过期),客户端会自动使用新 nonce 重试。

2.4 认证头解析与验证

2.4.1 解析 Authorization 头部
def parse_digest_auth_header(auth_header_value): \"\"\" 解析 Authorization: Digest ... 头部字符串 返回一个包含各参数的字典 \"\"\" if not auth_header_value or not auth_header_value.lower().startswith(\'digest \'): return None auth_parts = {} # 移除 \"Digest \" 前缀 value_str = auth_header_value[len(\'Digest \'):] # 使用正则表达式解析 key=\"value\" 或 key=value 对 # 这个正则表达式处理了带引号和不带引号的值 pattern = re.compile(r\'(\\w+)=(?:\"([^\"]*)\"|([^\\s,]*))\') for match in pattern.finditer(value_str): key = match.group(1) # 值可能在 group(2) (带引号) 或 group(3) (不带引号) val = match.group(2) if match.group(2) is not None else match.group(3) auth_parts[key] = val return auth_parts

客户端发送的 Authorization 头部是一个复杂的字符串(如 Digest username=\"admin001\", realm=\"MyProtectedSD\", nonce=\"a1b2c3\", ...),此函数通过正则表达式提取各参数,供后续验证使用。

2.4.2 验证认证响应
def verify_digest_response(self, auth_parts): \"\"\"校验客户端提供的 Digest 认证信息\"\"\" required_keys = [\'username\', \'realm\', \'nonce\', \'uri\', \'response\', \'qop\', \'nc\', \'cnonce\'] for key in required_keys: if key not in auth_parts: print(f\"Missing digest auth key: {key}\") return False username = auth_parts.get(\'username\') client_realm = auth_parts.get(\'realm\') client_nonce = auth_parts.get(\'nonce\') uri = auth_parts.get(\'uri\') client_response = auth_parts.get(\'response\') qop = auth_parts.get(\'qop\') nc = auth_parts.get(\'nc\') # nonce count cnonce = auth_parts.get(\'cnonce\') # client nonce algorithm = auth_parts.get(\'algorithm\', \'MD5\').upper() # 默认为 MD5 # 1. 校验 Realm if client_realm != REALM: print(f\"Realm mismatch: expected \'{REALM}\', got \'{client_realm}\'\") return False # 2. 校验 Nonce # 检查 nonce 是否由服务器发出且未过期 # 实际应用中,还需要检查 nc (nonce count) 以防止重放攻击 (nc 应该单调递增) # 这里简化处理:只检查 nonce 是否存在且未超时 if client_nonce not in active_nonces: print(f\"Invalid nonce: {client_nonce} (not issued by server)\") # 可以考虑发送 stale=true,让客户端用新 nonce 重试 return \"stale\" # 特殊返回值表示 nonce 过期 if time.time() - active_nonces[client_nonce] > NONCE_LIFETIME_SECONDS: print(f\"Nonce expired: {client_nonce}\") del active_nonces[client_nonce] # 删除过期的 nonce return \"stale\" # 特殊返回值表示 nonce 过期 # 3. 校验 Opaque (如果服务器在挑战中发送了) if \'opaque\' in auth_parts and auth_parts.get(\'opaque\') != OPAQUE: print(f\"Opaque mismatch\") return False # 4. 获取用户密码 (或预计算的 HA1) password = USERS_PASSWORDS.get(username) if not password: print(f\"Unknown user: {username}\") return False # 5. 计算 HA1 # HA1 = MD5(username:realm:password) ha1_str = f\"{username}:{REALM}:{password}\" ha1 = hashlib.md5(ha1_str.encode(\'utf-8\')).hexdigest() print(f\"Calculated HA1 for {username}: {ha1}\") # 6. 计算 HA2 # HA2 = MD5(method:uri) # 注意: self.command 是 HTTP 方法 (e.g., \"GET\") # uri 是客户端在 Authorization 头中提供的 URI ha2_str = f\"{self.command}:{uri}\" ha2 = hashlib.md5(ha2_str.encode(\'utf-8\')).hexdigest() print(f\"Calculated HA2 for {self.command}:{uri}: {ha2}\") # 7. 计算期望的 response # response = MD5(HA1:nonce:nc:cnonce:qop:HA2) if qop == \"auth\" or qop == \"auth-int\": # auth-int 需要校验 body,这里简化 expected_response_str = f\"{ha1}:{client_nonce}:{nc}:{cnonce}:{qop}:{ha2}\" else: # 如果 qop 不存在 (较老的 RFC 2069 规范,requests 不会这样) expected_response_str = f\"{ha1}:{client_nonce}:{ha2}\" expected_response = hashlib.md5(expected_response_str.encode(\'utf-8\')).hexdigest() print(f\"Expected response: {expected_response}\") print(f\"Client response: {client_response}\") # 8. 比较 response if client_response == expected_response: # 认证成功后,可以考虑使当前 nonce 失效(或严格检查 nc) # del active_nonces[client_nonce] # 如果 nonce 只能使用一次 return True else: print(\"Response mismatch.\") return False

此函数是服务端认证的核心逻辑,通过 8 步校验确保客户端的合法性:

  • 参数完整性:检查必要参数(如 usernamenonce)是否存在。
  • realm 匹配:确保客户端请求的认证域与服务端配置一致。
  • nonce 有效性:验证 nonce 是否由服务端生成且未过期(防止重放攻击)。
  • opaque 校验:确保客户端未篡改挑战参数。
  • 哈希计算:重新计算 HA1(用户密码哈希)和 HA2(请求信息哈希),并生成期望的 response,与客户端提供的 response 比较。

2.5 请求处理(do_GET 方法)

def do_GET(self): parsed_path = urlparse(self.path) # 只对 /sd 路径进行认证 if parsed_path.path == \'/sd\': auth_header = self.headers.get(\'Authorization\') if not auth_header: print(\"No Authorization header, sending 401 challenge.\") self.send_401_challenge() return auth_parts = parse_digest_auth_header(auth_header) if not auth_parts: print(\"Malformed Authorization header, sending 401 challenge.\") self.send_401_challenge() # 或发送 400 Bad Request return verification_result = self.verify_digest_response(auth_parts) if verification_result == \"stale\": print(\"Nonce was stale, sending 401 challenge with stale=true.\") self.send_401_challenge(stale=True) elif verification_result: print(\"Authentication successful!\") self.send_response(200) self.send_header(\'Content-type\', \'application/json\') self.end_headers() response_data = {\"message\": \"Welcome to the secure data area!\", \"user\": auth_parts.get(\'username\')} import json self.wfile.write(json.dumps(response_data).encode(\'utf-8\')) else: print(\"Authentication failed, sending 401 challenge again.\") # 认证失败,可以简单地再次发送 401 (可能用新的 nonce) # 或者根据具体策略,如果尝试次数过多可以发送 403 Forbidden self.send_401_challenge() else: self.send_response(200) self.send_header(\'Content-type\', \'text/plain\') self.end_headers() self.wfile.write(b\"This is an open area.\")

服务端通过 do_GET 方法处理请求:

  • 若请求路径为 /sd(受保护资源),则检查 Authorization 头部。
  • 无认证头或解析失败时,返回 401 挑战。
  • 认证成功后返回资源(如示例中的 JSON 数据)。
  • 其他路径(如根路径)直接返回公开内容。

三、关键技术点与安全增强

3.1 nonce 的时效性与重放攻击防范

  • 时效性nonce 仅在 NONCE_LIFETIME_SECONDS(5 分钟)内有效,过期后服务端删除记录,客户端需重新获取新 nonce
  • 重放攻击:通过 nc(请求计数)可以进一步防范 —— 客户端每次使用同一 nonce 时,nc 必须递增(如从 0000000100000002),服务端若发现 nc 未递增或重复,则判定为重放攻击。

3.2 密码存储的最佳实践

示例中直接存储明文密码(仅为演示),实际生产环境应存储预计算的 HA1MD5(username:realm:password))。这样即使数据库泄露,攻击者也无法直接获取密码明文,需结合 realmusername 才能计算 HA1,进一步增强安全性。

3.3 与 Basic 认证的对比

特性 Basic 认证 Digest 认证 密码传输方式 Base64 编码明文(可逆) 哈希值(不可逆) 防重放攻击 不支持 支持(通过 nonce、nc) 安全性 低(易被中间人截获明文) 高(无明文传输,动态随机数) 客户端支持 所有主流浏览器 所有主流浏览器

四、测试与验证

4.1 启动服务端

运行代码后,服务端监听 0.0.0.0:8001,保护路径为 /sd

4.2 测试请求

使用 requests 模拟客户端请求:

res = requests.get(\'http://127.0.0.1:8001/sd\',  auth=HTTPDigestAuth(\'admin001\', \'my_password_random_generation\'))print(f\"Status: {res.status_code}\")print(f\"Request Authorization: {res.request.headers[\'Authorization\']}\")print(f\"Headers: {res.headers}\")print(f\"Response Text: {res.text}\")

requests 客户端会自动处理挑战 - 响应流程,请求后打印,可见如下响应信息:

Status: 200Request Authorization: Digest username=\"admin001\", realm=\"MyProtectedSD\", nonce=\"f4b50139aeb242406e92e3a24a14f286\", uri=\"/sd\", response=\"72c8e7cf046193a3bce3fb80ec1ce4f6\", opaque=\"b5a7e1bf338b6f1d6c70204c64fd9473\", algorithm=\"MD5\", qop=\"auth\", nc=00000001, cnonce=\"0e22129e06725aa6\"Headers: {\'Server\': \'BaseHTTP/0.6 Python/3.12.9\', \'Date\': \'Thu, 22 May 2025 08:41:52 GMT\', \'Content-type\': \'application/json\'}Response Text: {\"message\": \"Welcome to the secure data area!\", \"user\": \"admin001\"}

服务端打印关键信息,可见客户端请求流程及认证明细情况:

No Authorization header, sending 401 challenge.127.0.0.1 - \"GET /sd HTTP/1.1\" 401 -Sent 401 challenge with new nonce: f4b50139aeb242406e92e3a24a14f286Calculated HA1 for admin001: fb5c1f99227711de645d65e5b091f978Calculated HA2 for GET:/sd: 7c7e535b35fb1070562dec4be2da7ee5Expected response: 72c8e7cf046193a3bce3fb80ec1ce4f6Client response: 72c8e7cf046193a3bce3fb80ec1ce4f6Authentication successful!127.0.0.1 - \"GET /sd HTTP/1.1\" 200 -

若认证成功,服务端返回文本:

{\"message\": \"Welcome to the secure data area!\", \"user\": \"admin001\"}

4.3 验证 nonce 过期

等待 5 分钟后,若使用之前的 nonce 再次请求,服务端会返回 stale=true 的挑战头,提示客户端使用新 nonce

五、服务端实现整体代码

import hashlibimport timeimport osimport refrom http.server import BaseHTTPRequestHandler, HTTPServerfrom urllib.parse import urlparse# --- 配置 ---SERVER_ADDRESS = \'0.0.0.0\' # 监听所有接口,方便测试SERVER_PORT = 8001REALM = \"MyProtectedSD\" # 保护域名,会显示在客户端的认证提示中# 存储用户名和密码(在实际应用中,密码应该哈希存储,这里为了演示方便直接存储)# 或者更好的方式是存储 HA1 = MD5(username:realm:password)# 例如: HA1 = hashlib.md5(f\"your_username_here:{REALM}:your_password_here\".encode()).hexdigest()USERS_PASSWORDS = { \"admin001\": \"my_password_random_generation\"}# 存储已发出的 nonce 及其创建时间,用于校验和防止重放# {nonce_value: creation_timestamp}# 实际应用中,nonce 应该有过期机制,并且用后即焚或严格校验 nc (nonce count)active_nonces = {}NONCE_LIFETIME_SECONDS = 300 # Nonce 有效期,例如 5 分钟# Opaque 值,服务器生成,客户端原样返回OPAQUE = hashlib.md5(os.urandom(16)).hexdigest()def generate_nonce(): \"\"\"生成一个唯一的 nonce 并记录创建时间\"\"\" nonce = hashlib.md5((os.urandom(16).hex() + str(time.time())).encode()).hexdigest() active_nonces[nonce] = time.time() # 记录 nonce 创建时间 return noncedef parse_digest_auth_header(auth_header_value): \"\"\" 解析 Authorization: Digest ... 头部字符串 返回一个包含各参数的字典 \"\"\" if not auth_header_value or not auth_header_value.lower().startswith(\'digest \'): return None auth_parts = {} # 移除 \"Digest \" 前缀 value_str = auth_header_value[len(\'Digest \'):] # 使用正则表达式解析 key=\"value\" 或 key=value 对 # 这个正则表达式处理了带引号和不带引号的值 pattern = re.compile(r\'(\\w+)=(?:\"([^\"]*)\"|([^\\s,]*))\') for match in pattern.finditer(value_str): key = match.group(1) # 值可能在 group(2) (带引号) 或 group(3) (不带引号) val = match.group(2) if match.group(2) is not None else match.group(3) auth_parts[key] = val return auth_partsclass DigestAuthHandler(BaseHTTPRequestHandler): def send_401_challenge(self, stale=False): \"\"\"发送 401 Unauthorized 响应和 WWW-Authenticate 挑战头\"\"\" self.send_response(401) self.send_header(\'Content-type\', \'text/plain\') nonce = generate_nonce() # 生成新 nonce auth_challenge = f\'Digest realm=\"{REALM}\", qop=\"auth\", nonce=\"{nonce}\", opaque=\"{OPAQUE}\", algorithm=\"MD5\"\' if stale: auth_challenge += \', stale=\"true\"\' # 标记旧 nonce 过期,提示客户端使用新 nonce self.send_header(\'WWW-Authenticate\', auth_challenge) self.end_headers() self.wfile.write(b\"Authentication required.\") print(f\"Sent 401 challenge with new nonce: {nonce}\") def verify_digest_response(self, auth_parts): \"\"\"校验客户端提供的 Digest 认证信息\"\"\" required_keys = [\'username\', \'realm\', \'nonce\', \'uri\', \'response\', \'qop\', \'nc\', \'cnonce\'] for key in required_keys: if key not in auth_parts: print(f\"Missing digest auth key: {key}\") return False username = auth_parts.get(\'username\') client_realm = auth_parts.get(\'realm\') client_nonce = auth_parts.get(\'nonce\') uri = auth_parts.get(\'uri\') client_response = auth_parts.get(\'response\') qop = auth_parts.get(\'qop\') nc = auth_parts.get(\'nc\') # nonce count cnonce = auth_parts.get(\'cnonce\') # client nonce algorithm = auth_parts.get(\'algorithm\', \'MD5\').upper() # 默认为 MD5 # 1. 校验 Realm if client_realm != REALM: print(f\"Realm mismatch: expected \'{REALM}\', got \'{client_realm}\'\") return False # 2. 校验 Nonce # 检查 nonce 是否由服务器发出且未过期 # 实际应用中,还需要检查 nc (nonce count) 以防止重放攻击 (nc 应该单调递增) # 这里简化处理:只检查 nonce 是否存在且未超时 if client_nonce not in active_nonces: print(f\"Invalid nonce: {client_nonce} (not issued by server)\") # 可以考虑发送 stale=true,让客户端用新 nonce 重试 return \"stale\" # 特殊返回值表示 nonce 过期 if time.time() - active_nonces[client_nonce] > NONCE_LIFETIME_SECONDS: print(f\"Nonce expired: {client_nonce}\") del active_nonces[client_nonce] # 删除过期的 nonce return \"stale\" # 特殊返回值表示 nonce 过期 # 3. 校验 Opaque (如果服务器在挑战中发送了) if \'opaque\' in auth_parts and auth_parts.get(\'opaque\') != OPAQUE: print(f\"Opaque mismatch\") return False # 4. 获取用户密码 (或预计算的 HA1) password = USERS_PASSWORDS.get(username) if not password: print(f\"Unknown user: {username}\") return False # 5. 计算 HA1 # HA1 = MD5(username:realm:password) ha1_str = f\"{username}:{REALM}:{password}\" ha1 = hashlib.md5(ha1_str.encode(\'utf-8\')).hexdigest() print(f\"Calculated HA1 for {username}: {ha1}\") # 6. 计算 HA2 # HA2 = MD5(method:uri) # 注意: self.command 是 HTTP 方法 (e.g., \"GET\") # uri 是客户端在 Authorization 头中提供的 URI ha2_str = f\"{self.command}:{uri}\" ha2 = hashlib.md5(ha2_str.encode(\'utf-8\')).hexdigest() print(f\"Calculated HA2 for {self.command}:{uri}: {ha2}\") # 7. 计算期望的 response # response = MD5(HA1:nonce:nc:cnonce:qop:HA2) if qop == \"auth\" or qop == \"auth-int\": # auth-int 需要校验 body,这里简化 expected_response_str = f\"{ha1}:{client_nonce}:{nc}:{cnonce}:{qop}:{ha2}\" else: # 如果 qop 不存在 (较老的 RFC 2069 规范,requests 不会这样) expected_response_str = f\"{ha1}:{client_nonce}:{ha2}\" expected_response = hashlib.md5(expected_response_str.encode(\'utf-8\')).hexdigest() print(f\"Expected response: {expected_response}\") print(f\"Client response: {client_response}\") # 8. 比较 response if client_response == expected_response: # 认证成功后,可以考虑使当前 nonce 失效(或严格检查 nc) # del active_nonces[client_nonce] # 如果 nonce 只能使用一次 return True else: print(\"Response mismatch.\") return False def do_GET(self): parsed_path = urlparse(self.path) # 只对 /sd 路径进行认证 if parsed_path.path == \'/sd\': auth_header = self.headers.get(\'Authorization\') if not auth_header: print(\"No Authorization header, sending 401 challenge.\") self.send_401_challenge() return auth_parts = parse_digest_auth_header(auth_header) if not auth_parts: print(\"Malformed Authorization header, sending 401 challenge.\") self.send_401_challenge() # 或发送 400 Bad Request return verification_result = self.verify_digest_response(auth_parts) if verification_result == \"stale\": print(\"Nonce was stale, sending 401 challenge with stale=true.\") self.send_401_challenge(stale=True) elif verification_result: print(\"Authentication successful!\") self.send_response(200) self.send_header(\'Content-type\', \'application/json\') self.end_headers() response_data = {\"message\": \"Welcome to the secure data area!\", \"user\": auth_parts.get(\'username\')} import json self.wfile.write(json.dumps(response_data).encode(\'utf-8\')) else: print(\"Authentication failed, sending 401 challenge again.\") # 认证失败,可以简单地再次发送 401 (可能用新的 nonce) # 或者根据具体策略,如果尝试次数过多可以发送 403 Forbidden self.send_401_challenge() else: self.send_response(200) self.send_header(\'Content-type\', \'text/plain\') self.end_headers() self.wfile.write(b\"This is an open area.\") def log_message(self, format, *args): \"\"\"覆盖默认日志,方便调试\"\"\" print(f\"{self.address_string()} - {format % args}\")def run_server(server_class=HTTPServer, handler_class=DigestAuthHandler, addr=SERVER_ADDRESS, port=SERVER_PORT): server_address = (addr, port) httpd = server_class(server_address, handler_class) print(f\"Starting Digest Auth server on {addr}:{port}...\") print(f\"Protected path: /sd\") print(f\"Test with username: \'{USERS_PASSWORDS.keys()}\', password: \'{USERS_PASSWORDS.values()}\'\") try: httpd.serve_forever() except KeyboardInterrupt: print(\"\\nServer shutting down.\") finally: httpd.server_close()if __name__ == \'__main__\': run_server()

小总结

HTTP Digest 认证通过哈希算法和动态随机数(nonce)解决了 Basic 认证的明文传输问题,是轻量级场景下的安全认证方案。本文结合代码详细解析了其核心流程(挑战 - 响应)和服务端实现逻辑(nonce 管理、哈希计算、响应验证),并强调了生产环境中的安全增强点(如存储 HA1、校验 nc)。实际应用中,建议结合 HTTPS 进一步加密传输过程,以达到更高的安全性。

————————————————

Java猿社区—Http digest authentication 请求代码最全示例 - 简书

HTTP认证之摘要认证——Digest(二) - xiaoxiaotank - 博客园

HTTP的几种认证方式之DIGEST 认证(摘要认证) - wenbin_ouyang - 博客园

HTTP Authentication之Basic认证、Digest认证

http digest鉴权流程

python http 身份认证简介