You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python 3 Fernet使用轻微修改的密钥可成功解密的技术疑问

问题背景

以下是测试用的Python代码:

def encrypt_message(message, key) -> bytes:
  """Encrypts a message using Fernet."""
  f = Fernet(key)
  encrypted_message = f.encrypt(message.encode())
  return encrypted_message


def decrypt_message(encrypted_message, key) -> str:
  """Decrypts a message using Fernet."""
  f = Fernet(key)
  decrypted_message = f.decrypt(encrypted_message).decode()
  return decrypted_message


message_ = "This is a secret message!"

encrypted = encrypt_message(message_, b"HeYt_XX4wRLvnDP9fC9-7hlMgasQ270tpWSrjKy0jRE=")
# 每次加密的密文会变化,这是正常现象
print("Encrypted:", encrypted)

decrypted = decrypt_message(encrypted, b"HeYt_XX4wRLvnDP9fC9-7hlMgasQ270tpWSrjKy0jRF=")
print("Decrypted:", decrypted)

运行后发现一个奇怪的现象:调用decrypt_message时,把解密密钥末尾的E替换成F/G/H,解密依然能成功;但替换成I时解密失败。按对称加密的常识,加解密必须用完全相同的密钥,对此感到困惑,寻求解释。

测试输出结果:

Encrypted: b'gAAAAABmcc7eNiwRP4lYl7gM0mthNdlvYsPUs_nttJMchmZ_mfJNMS4D2BC3QZHZZiZ7vrPhPwvBqK2DDOXSuB5wQkk9t-tU0PYefGvNy2IoDZNGNQMcdc0='
Decrypted: This is a secret message!
Process finished with exit code 0
问题原因解析

你遇到的这个现象,核心是Fernet密钥的Base64编码特性在起作用:

  1. Fernet密钥的本质:Fernet要求密钥是32字节的二进制数据,我们传入的字符串是这个二进制密钥经过URL-safe Base64编码后的结果。32字节等于256位,Base64每4个字符对应3字节(24位),256位的二进制数据需要43个Base64字符(42个字符对应252位,最后1个字符补充剩下的4位),再加上1个=填充符,凑成44个字符的标准格式。

  2. 末尾字符的解码逻辑:你修改的末尾字符(E/F/G/H/I)属于Base64编码的最后一个有效字符,它只需要提供4位数据就能补全256位,剩下的2位会被填充符=忽略。看Base64的字符编码规则(URL-safe版本和标准一致,仅替换+为-、/为_):

    • E对应二进制000100,取前4位是0001
    • F对应000101,前4位0001
    • G对应000110,前4位0001
    • H对应000111,前4位0001
    • I对应001000,前4位0010

    所以当你把E换成F/G/H时,解码得到的32字节二进制密钥和原密钥完全一致——因为我们只取了这些字符的前4位,后2位被填充忽略了。而换成I时,前4位变成了0010,解码后的二进制密钥和原密钥产生了差异,导致Fernet的HMAC签名验证失败,解密就无法成功了。

  3. 对称加密的规则没有被打破:你以为用了不同的密钥,但实际上Fernet真正使用的二进制密钥是一样的,这才是解密成功的原因。一旦二进制密钥真的不同(比如换成I的情况),对称加密“加解密密钥必须完全一致”的规则就生效了,解密必然失败。

内容的提问来源于stack exchange,提问作者OGM

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 01:24:52