Ticket #18852: 18852.patch

File 18852.patch, 7.1 KB (added by Aymeric Augustin, 12 years ago)
  • django/core/signing.py

    diff --git a/django/core/signing.py b/django/core/signing.py
    index 6fc76bc..61faee2 100644
    a b start of the base64 JSON.  
    3232There are 65 url-safe characters: the 64 used by url-safe base64 and the ':'.
    3333These functions make use of all of them.
    3434"""
     35
    3536from __future__ import unicode_literals
    3637
    3738import base64
    from django.conf import settings  
    4344from django.core.exceptions import ImproperlyConfigured
    4445from django.utils import baseconv
    4546from django.utils.crypto import constant_time_compare, salted_hmac
    46 from django.utils.encoding import smart_bytes
     47from django.utils.encoding import force_str, force_text
    4748from django.utils.importlib import import_module
    4849
    4950
    class SignatureExpired(BadSignature):  
    6263
    6364
    6465def b64_encode(s):
    65     return base64.urlsafe_b64encode(smart_bytes(s)).decode('ascii').strip('=')
     66    return base64.urlsafe_b64encode(s).strip(b'=')
    6667
    6768
    6869def b64_decode(s):
    69     pad = '=' * (-len(s) % 4)
    70     return base64.urlsafe_b64decode(smart_bytes(s + pad)).decode('ascii')
     70    pad = b'=' * (-len(s) % 4)
     71    return base64.urlsafe_b64decode(s + pad)
    7172
    7273
    7374def base64_hmac(salt, value, key):
    def dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer,  
    116117    value or re-using a salt value across different parts of your
    117118    application without good cause is a security risk.
    118119    """
    119     data = serializer().dumps(obj)
     120    data = serializer().dumps(obj).encode()
    120121
    121122    # Flag for if it's been compressed or not
    122123    is_compressed = False
    123124
    124125    if compress:
    125126        # Avoid zlib dependency unless compress is being used
    126         compressed = zlib.compress(smart_bytes(data))
     127        compressed = zlib.compress(data)
    127128        if len(compressed) < (len(data) - 1):
    128129            data = compressed
    129130            is_compressed = True
    130131    base64d = b64_encode(data)
    131132    if is_compressed:
    132         base64d = '.' + base64d
     133        base64d = b'.' + base64d
    133134    return TimestampSigner(key, salt=salt).sign(base64d)
    134135
    135136
    def loads(s, key=None, salt='django.core.signing', serializer=JSONSerializer, ma  
    137138    """
    138139    Reverse of dumps(), raises BadSignature if signature fails
    139140    """
    140     base64d = TimestampSigner(key, salt=salt).unsign(s, max_age=max_age)
     141    # TimestampSigner.unsign always returns unicode but base64 and zlib
     142    # compression operate on bytes, so we  decode() here.
     143    base64d = TimestampSigner(key, salt=salt).unsign(s, max_age=max_age).encode()
    141144    decompress = False
    142     if base64d[0] == '.':
     145    if base64d[0] == b'.':
    143146        # It's compressed; uncompress it first
    144147        base64d = base64d[1:]
    145148        decompress = True
    146149    data = b64_decode(base64d)
    147150    if decompress:
    148151        data = zlib.decompress(data)
    149     return serializer().loads(data)
     152    return serializer().loads(data.decode())
    150153
    151154
    152155class Signer(object):
     156
    153157    def __init__(self, key=None, sep=':', salt=None):
    154         self.sep = sep
    155         self.key = key or settings.SECRET_KEY
    156         self.salt = salt or ('%s.%s' %
    157             (self.__class__.__module__, self.__class__.__name__))
     158        # Use of native strings in all versions of Python
     159        self.sep = str(sep)
     160        self.key = str(key or settings.SECRET_KEY)
     161        self.salt = str(salt or
     162            '%s.%s' % (self.__class__.__module__, self.__class__.__name__))
    158163
    159164    def signature(self, value):
    160         return base64_hmac(self.salt + 'signer', value, self.key)
     165        signature = base64_hmac(self.salt + 'signer', value, self.key)
     166        # Convert the signature from bytes to str only on Python 3
     167        return force_str(signature)
    161168
    162169    def sign(self, value):
    163         return '%s%s%s' % (value, self.sep, self.signature(value))
     170        value = force_str(value)
     171        return str('%s%s%s') % (value, self.sep, self.signature(value))
    164172
    165173    def unsign(self, signed_value):
     174        signed_value = force_str(signed_value)
    166175        if not self.sep in signed_value:
    167176            raise BadSignature('No "%s" found in value' % self.sep)
    168177        value, sig = signed_value.rsplit(self.sep, 1)
    169178        if constant_time_compare(sig, self.signature(value)):
    170             return value
     179            return force_text(value)
    171180        raise BadSignature('Signature "%s" does not match' % sig)
    172181
    173182
    class TimestampSigner(Signer):  
    177186        return baseconv.base62.encode(int(time.time()))
    178187
    179188    def sign(self, value):
    180         value = '%s%s%s' % (value, self.sep, self.timestamp())
    181         return '%s%s%s' % (value, self.sep, self.signature(value))
     189        value = force_str(value)
     190        value = str('%s%s%s') % (value, self.sep, self.timestamp())
     191        return super(TimestampSigner, self).sign(value)
    182192
    183193    def unsign(self, value, max_age=None):
    184194        result =  super(TimestampSigner, self).unsign(value)
  • tests/regressiontests/signing/tests.py

    diff --git a/tests/regressiontests/signing/tests.py b/tests/regressiontests/signing/tests.py
    index 7145ec8..05c8b3d 100644
    a b import time  
    44
    55from django.core import signing
    66from django.test import TestCase
     7from django.utils.encoding import force_str
    78from django.utils import six
    8 from django.utils.encoding import force_text
    99
    1010
    1111class TestSigner(TestCase):
    class TestSigner(TestCase):  
    2222            self.assertEqual(
    2323                signer.signature(s),
    2424                signing.base64_hmac(signer.salt + 'signer', s,
    25                     'predictable-secret')
     25                    'predictable-secret').decode()
    2626            )
    2727            self.assertNotEqual(signer.signature(s), signer2.signature(s))
    2828
    class TestSigner(TestCase):  
    3232        self.assertEqual(
    3333            signer.signature('hello'),
    3434                signing.base64_hmac('extra-salt' + 'signer',
    35                 'hello', 'predictable-secret'))
     35                'hello', 'predictable-secret').decode()
     36            )
    3637        self.assertNotEqual(
    3738            signing.Signer('predictable-secret', salt='one').signature('hello'),
    3839            signing.Signer('predictable-secret', salt='two').signature('hello'))
    class TestSigner(TestCase):  
    4041    def test_sign_unsign(self):
    4142        "sign/unsign should be reversible"
    4243        signer = signing.Signer('predictable-secret')
    43         examples = (
     44        examples = [
    4445            'q;wjmbk;wkmb',
    4546            '3098247529087',
    4647            '3098247:529:087:',
    4748            'jkw osanteuh ,rcuh nthu aou oauh ,ud du',
    4849            '\u2019',
    49         )
     50        ]
     51        if not six.PY3:
     52            examples.append(b'a byte string')
    5053        for example in examples:
    51             self.assertNotEqual(
    52                 force_text(example), force_text(signer.sign(example)))
    53             self.assertEqual(example, signer.unsign(signer.sign(example)))
     54            signed = signer.sign(example)
     55            self.assertIsInstance(signed, str)
     56            self.assertNotEqual(force_str(example), signed)
     57            self.assertEqual(example, signer.unsign(signed))
    5458
    5559    def unsign_detects_tampering(self):
    5660        "unsign should raise an exception if the value has been tampered with"
Back to Top