Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django urlsafe base64 decoding with decryption

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:

_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:

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?

like image 923
Enchantner Avatar asked Feb 09 '10 14:02

Enchantner


1 Answers

The problem is that b64decode quite explicitly can only take bytes (a string), not 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.

>>> base64.urlsafe_b64decode(uenc.encode("ascii"))
"Hi, I'm a string"
like image 87
Jeffrey Harris Avatar answered Sep 27 '22 22:09

Jeffrey Harris