Django 网址安全的 base64 解码与解密

20 投票
2 回答
24698 浏览
提问于 2025-04-15 19:04

我正在为用户注册写一个自己的验证码系统。所以我需要创建一个合适的URL来接收生成的验证码图片。生成的过程是这样的:

_cipher = cipher.new(settings.CAPTCHA_SECRET_KEY, cipher.MODE_ECB)
_encrypt_block = lambda block: _cipher.encrypt(block + ' ' * (_cipher.block_size - len(block) % _cipher.block_size)) 
#...
a = (self.rightnum, self.animal_type[1])
serialized = pickle.dumps(a)
encrypted = _encrypt_block(serialized)
safe_url = urlsafe_b64encode(encrypted)

但是当我尝试在视图函数中通过GET请求接收这个密钥时,urlsafe_b64decode()出错了,错误信息是“字符映射必须返回整数、None或unicode”。

def captcha(request):
  try:
    key = request.REQUEST['key']
    decoded = urlsafe_b64decode(key)
    decrypted = _decrypt_block(decoded)
    deserialized = pickle.loads(decrypted)
    return HttpResponse(deserialized)
  except KeyError: 
    return HttpResponseBadRequest()

我发现urlsafe_b64encode的输出是一个字符串,但GET请求返回的是一个unicode对象(尽管它是正确的字符串)。使用str()没有帮助(它在django内部返回解码错误),而如果我使用key.repr,它就能工作,但解密器却报错说“输入字符串的长度必须是16的倍数”。在一个测试文件中,这一切都能完美运行,我搞不懂哪里出了问题。

2 个回答

3

我解决了这个问题!

deserialized = pickle.loads(captcha_decrypt(urlsafe_b64decode(key.encode('ascii'))))
return HttpResponse(str(deserialized))

不过我还是不明白,为什么第一次不管用。

41

问题在于,b64decode 这个函数只能处理字节(也就是字符串),而不能处理 Unicode。

>>> import base64
>>> test = "Hi, I'm a string"
>>> enc = base64.urlsafe_b64encode(test)
>>> enc
'SGksIEknbSBhIHN0cmluZw=='
>>> uenc = unicode(enc)
>>> base64.urlsafe_b64decode(enc)
"Hi, I'm a string"
>>> base64.urlsafe_b64decode(uenc)
Traceback (most recent call last):
...
TypeError: character mapping must return integer, None or unicode

因为你知道你的数据只包含 ASCII 数据(这正是 base64encode 返回的内容),所以把你的 Unicode 字符编码成 ASCII 或 UTF-8 字节是安全的,这些字节会和你预期的 ASCII 一样。

>>> base64.urlsafe_b64decode(uenc.encode("ascii"))
"Hi, I'm a string"

撰写回答