持有正确密钥解密数据库加密消息时触发cryptography.fernet.InvalidToken错误的排查求助
解密Fernet加密消息时触发Invalid Token?看这几个常见问题
从你贴的错误栈能看出来,核心问题是Base64编码格式被破坏:binascii.Error: Invalid base64-encoded string: number of data characters (201) cannot be 1 more than a multiple of 4,这说明数据库里存储的加密串要么被截断、要么被篡改了,才导致Fernet无法正常解析,最终抛出Invalid Token错误。结合你的代码,我整理了几个大概率的失误点:
1. 数据库字段类型/长度选错了
Fernet加密后的结果是URL-safe的Base64字节串,如果你存储时没选对字段类型,很容易搞坏它:
- 别用长度受限的字段:比如
VARCHAR(200)这种,要是加密后的token长度超过限制,直接就被截断了(你这里的字符数是201,刚好卡线,大概率是字段长度不够)。换成TEXT或者BLOB类型更稳妥。 - 统一转码逻辑:加密后得到的是字节串,要存成字符串的话,统一用
encrypted_token.decode('utf-8');读取解密时再用msg.message_text.encode('utf-8')——你代码里这么做了,但要确认加密存储时是不是也用了同样的编码。
2. 存储/读取时混入了额外字符
比如加密后的token被不小心加了换行、空格,或者数据库读取时自动加了转义字符:
- 直接去数据库查看
message_text字段的内容,对比加密时生成的原始token,看看有没有多出来的空格、换行,或者被截断的痕迹。 - 解密前可以先清理字符串:比如
msg.message_text.strip(),去掉首尾的空白字符,避免这些小细节搞砸Base64解码。
3. 密钥读取时出了问题(别不信,哪怕你说密钥固定)
你代码里打印了key,可以核对这两点:
- 密钥的长度应该是44个字符(Fernet密钥是32字节原始数据转成URL-safe Base64后的结果),如果长度不对,说明读取密钥时读进了额外内容(比如文件末尾的换行符)。
- 确认密钥文件
encryption_key里只有密钥本身,没有多余的换行或空格——生成密钥时你用f.write(key)不会加换行,但如果之后手动编辑过文件,就可能出问题。
4. ORM或数据库驱动的自动转义坑
有些ORM(比如SQLAlchemy)或者数据库驱动会自动对字符串做转义处理,把特殊字符转成转义序列,导致存储的token被篡改:
- 如果用的是SQLAlchemy,试试把
message_text字段改成LargeBinary类型,直接存字节串,绕过字符串转义的问题。加密时存encrypted_token(字节),读取时直接用msg.message_text解密,不用再encode()。
快速调试技巧
- 解密前先打印
msg.message_text的长度和内容,和加密时生成的原始token对比,看是不是一致。 - 拿加密时生成的原始token写个测试脚本直接解密,看能不能成功——如果能,那肯定是数据库存储/读取的问题;如果不能,那就是密钥或者加密逻辑的问题。
给你贴个正确的加密存储示例,你可以对照着检查自己的代码:
# 加密并存储的代码示例 from datetime import datetime from your_module import Message, db with open('encryption_key', 'rb') as f: key = f.read() crypter = Fernet(key) # 加密消息 original_msg = "Hello World" encrypted_bytes = crypter.encrypt(original_msg.encode('utf-8')) # 存储到数据库(转成字符串) new_msg = Message( message_sender="user1", message_text=encrypted_bytes.decode('utf-8'), message_room="room1", message_time=datetime.utcnow() ) db.session.add(new_msg) db.session.commit()
内容的提问来源于stack exchange,提问作者Adam Demo_Fighter
相关产品推荐
相关产品推荐

