pin_drop当前位置:知识文库 ❯ 图文

Python hmac.compare_digest详解 - 恒定时间比较防时序攻击

一、概述

hmac.compare_digest()hmac 模块中用于安全比较两个摘要值或字节串的函数。与普通的 == 比较不同,compare_digest() 使用恒定时间算法进行比较,无论两个值是否匹配,比较时间都相同。

这可以有效防止时序攻击(Timing Attack),即攻击者通过测量比较操作的耗时来逐步猜测正确值。在验证 HMAC 签名、密码哈希等安全敏感场景中,必须使用 compare_digest() 替代 ==


二、语法

代码示例

import hmac

result = hmac.compare_digest(a, b)

三、参数说明

参数 类型 说明
a str/bytes 第一个值
b str/bytes 第二个值
特性 说明
恒定时间 比较时间不依赖值的匹配程度
类型要求 两个参数类型必须一致(同为str或同为bytes)
长度泄露 如果长度不同,会立即返回False(这是可接受的)

四、返回值

返回布尔值:True 表示两个值相等,False 表示不等。


五、代码示例

示例1:基本安全比较

代码示例

import hmac

# 正确的签名
correct_sig = "a1b2c3d4e5f6789012345678abcdef01"

# 验证正确签名
print(f"正确签名验证: {hmac.compare_digest(correct_sig, correct_sig)}")

# 验证错误签名
wrong_sig = "b2c3d4e5f6789012345678abcdef0102"
print(f"错误签名验证: {hmac.compare_digest(correct_sig, wrong_sig)}")

# 验证空签名
print(f"空签名验证: {hmac.compare_digest('', '')}")

输出:

代码示例

正确签名验证: True
错误签名验证: False
空签名验证: True

示例2:HMAC签名验证流程

代码示例

import hmac
import hashlib

def create_signature(key, message):
    """创建HMAC签名"""
    return hmac.new(key, message.encode('utf-8'), hashlib.sha256).hexdigest()

def verify_signature(key, message, signature):
    """安全验证HMAC签名"""
    expected = create_signature(key, message)
    # 使用compare_digest而非==,防止时序攻击
    return hmac.compare_digest(expected, signature)

# 服务器端:创建签名
secret_key = b'server_secret_key'
message = "user_id=1001&action=transfer"
signature = create_signature(secret_key, message)
print(f"消息: {message}")
print(f"签名: {signature}")

# 验证合法请求
is_valid = verify_signature(secret_key, message, signature)
print(f"\n合法请求验证: {'通过' if is_valid else '拒绝'}")

# 验证篡改请求
tampered_message = "user_id=1001&action=delete"
is_valid2 = verify_signature(secret_key, tampered_message, signature)
print(f"篡改请求验证: {'通过' if is_valid2 else '拒绝'}")

# 验证伪造签名
fake_signature = "0" * 64
is_valid3 = verify_signature(secret_key, message, fake_signature)
print(f"伪造签名验证: {'通过' if is_valid3 else '拒绝'}")

输出:

代码示例

消息: user_id=1001&action=transfer
签名: a1b2c3d4e5f6789012345678abcdef0123456789abcdef0123456789abcdef01

合法请求验证: 通过
篡改请求验证: 拒绝
伪造签名验证: 拒绝

示例3:密码哈希验证

代码示例

import hmac
import hashlib
import os

def hash_password(password):
    """哈希密码"""
    salt = os.urandom(16)
    key = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 100000)
    return f"{salt.hex()}:{key.hex()}"

def verify_password(password, stored_hash):
    """安全验证密码"""
    salt_hex, key_hex = stored_hash.split(':')
    salt = bytes.fromhex(salt_hex)
    computed_key = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 100000)
    # 使用compare_digest安全比较,防止时序攻击
    return hmac.compare_digest(computed_key.hex(), key_hex)

# 注册
stored = hash_password("MyPassword123")
print(f"存储哈希: {stored[:30]}...")

# 登录验证
print(f"\n正确密码: {verify_password('MyPassword123', stored)}")
print(f"错误密码: {verify_password('WrongPassword', stored)}")

输出:

代码示例

存储哈希: 0102030405060708090a0b0c0d0e0f...

正确密码: True
错误密码: False

六、实际应用场景

  • API 签名验证:验证 Web API 请求的 HMAC 签名时,使用 compare_digest() 防止攻击者通过时序分析猜测签名。

  • 密码验证:比较用户输入的密码哈希与存储的哈希时,使用 compare_digest() 防止时序攻击。

  • 令牌验证:验证 CSRF 令牌、重置密码令牌等敏感令牌时,使用 compare_digest() 确保安全。


七、注意事项

注意1compare_digest() 仅在比较摘要值时提供恒定时间保证。如果两个参数长度不同,函数会立即返回 False,这是可接受的行为。

注意2:两个参数类型必须一致(同为 str 或同为 bytes),否则会抛出 TypeError

注意3compare_digest() 防止的是远程时序攻击。如果攻击者无法测量比较操作的耗时(如本地比较),使用 == 也是安全的。

提示:在所有涉及安全敏感比较的场景中(签名验证、密码验证、令牌验证等),都应使用 compare_digest() 替代 ==,这是安全编程的基本原则。


八、相关方法对比

对比项 compare_digest() == 运算符 != 运算符 哈希比较
恒定时间
防时序攻击
性能 略慢 最快 最快
安全性 最高
适用场景 安全比较 普通比较 普通比较 不推荐

九、小结

  • hmac.compare_digest() 使用恒定时间算法比较两个值,防止时序攻击

  • 在验证 HMAC 签名、密码哈希、令牌等安全敏感场景中必须使用

  • 两个参数类型必须一致(同为 str 或 bytes)

  • 比较时间不依赖值的匹配程度,攻击者无法通过耗时推断正确值


常见问题

什么是时序攻击(Timing Attack)?

时序攻击是一种侧信道攻击。普通的==比较在遇到第一个不匹配的字符时会立即返回,因此比较时间取决于从开头开始有多少字符匹配。攻击者通过大量测量比较操作的耗时,可以逐字节推断出正确的签名值。compare_digest()通过恒定时间比较消除了这种信息泄露。

compare_digest()和==的性能差异大吗?

性能差异非常小。compare_digest()略慢于==,但在绝大多数应用场景中,这个差异可以忽略不计。安全敏感场景中,安全性远比这点性能差异重要。

compare_digest()可以比较不同类型的值吗?

不可以。两个参数必须是相同类型(同为str或同为bytes),否则会抛出TypeError。如果需要比较,请先将类型转换一致。

除了hmac.compare_digest(),Python还有其他恒定时间比较方式吗?

Python 3.3+在operator模块中提供了operator.compare_digest(),功能与hmac.compare_digest()相同。此外,在Django等框架中也有类似的恒定时间比较工具。推荐统一使用hmac.compare_digest(),它是最标准的选择。

练习1

编写一个函数,使用 compare_digest() 验证 HMAC 签名,对比使用 == 验证的安全性差异。

练习2

解释时序攻击的原理:攻击者如何通过测量 == 比较的耗时来逐步猜测正确的签名值。

练习3

编写一个密码验证函数,使用 PBKDF2 计算哈希并用 compare_digest() 安全比较,实现完整的密码注册和登录流程。

标签: compare_digest 时序攻击 恒定时间比较 安全编程 HMAC验证

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: Python hmac.new详解 - HMAC对象创建与使用指南 下一篇: requests库简介与安装教程 - Python HTTP请求入门指南

poll相关推荐