Django urlsafe base64解密与解密

时间:2023-02-07 18:35:00

I'm writing my own captcha system for user registration. So I need to create a suitable URL for receiving generated captcha pictures. Generation looks like this:

我正在为用户注册编写自己的验证码系统。所以我需要创建一个合适的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)

But then I'm trying to receive this key via GET request in the view function, it fails on urlsafe_b64decode() with "character mapping must return integer, None or unicode" error:

但后来我试图通过视图函数中的GET请求接收此密钥,它在urlsafe_b64decode()上失败,“字符映射必须返回整数,无或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()

I found that on the output of urlsafe_b64encode there is an str, but GET request returns a unicode object (nevertheless it's a right string). Str() didn't help (it returns decode error deep inside django), and if I use key.repr it works, but decryptor doesn't work with an error "Input strings must be a multiple of 16 in length". Inside a test file all this construction works perfectly, I can't understand, what's wrong?

我发现在urlsafe_b64encode的输出上有一个str,但是GET请求返回一个unicode对象(不过它是一个正确的字符串)。 Str()没有帮助(它在django内部返回解码错误),如果我使用key.repr它可以工作,但解密器不能使用错误“输入字符串必须是16的倍数”。在测试文件里面所有这些结构都很完美,我无法理解,有什么不对?

2 个解决方案

#1


32  

The problem is that b64decode quite explicitly can only take bytes (a string), not unicode.

问题是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

Since you know that your data only contains ASCII data (that's what base64encode will return), it should be safe to encode your unicode code points as ASCII or UTF-8 bytes, those bytes will be equivalent to the ASCII you expected.

既然您知道您的数据只包含ASCII数据(这就是base64encode将返回的数据),那么将您的unicode代码点编码为ASCII或UTF-8字节应该是安全的,这些字节将等同于您期望的ASCII。

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

#2


3  

I solved the problem!

我解决了这个问题!

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

But still I don't understand, why it didn't work first time.

但我仍然不明白,为什么它第一次没用。

#1


32  

The problem is that b64decode quite explicitly can only take bytes (a string), not unicode.

问题是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

Since you know that your data only contains ASCII data (that's what base64encode will return), it should be safe to encode your unicode code points as ASCII or UTF-8 bytes, those bytes will be equivalent to the ASCII you expected.

既然您知道您的数据只包含ASCII数据(这就是base64encode将返回的数据),那么将您的unicode代码点编码为ASCII或UTF-8字节应该是安全的,这些字节将等同于您期望的ASCII。

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

#2


3  

I solved the problem!

我解决了这个问题!

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

But still I don't understand, why it didn't work first time.

但我仍然不明白,为什么它第一次没用。