Django 网址安全的 base64 解码与解密
我正在为用户注册写一个自己的验证码系统。所以我需要创建一个合适的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"