Fixed #31359 -- Deprecated get_random_string() calls without an explicit length.
This commit is contained in:
parent
5cc2c63f90
commit
e663f695fb
|
@ -185,7 +185,8 @@ class BasePasswordHasher:
|
||||||
|
|
||||||
def salt(self):
|
def salt(self):
|
||||||
"""Generate a cryptographically secure nonce salt in ASCII."""
|
"""Generate a cryptographically secure nonce salt in ASCII."""
|
||||||
return get_random_string()
|
# 12 returns a 71-bit value, log_2((26+26+10)^12) =~ 71 bits
|
||||||
|
return get_random_string(12)
|
||||||
|
|
||||||
def verify(self, password, encoded):
|
def verify(self, password, encoded):
|
||||||
"""Check if the given password is correct."""
|
"""Check if the given password is correct."""
|
||||||
|
|
|
@ -341,7 +341,7 @@ class DatabaseCreation(BaseDatabaseCreation):
|
||||||
password = self._test_settings_get('PASSWORD')
|
password = self._test_settings_get('PASSWORD')
|
||||||
if password is None and self._test_user_create():
|
if password is None and self._test_user_create():
|
||||||
# Oracle passwords are limited to 30 chars and can't contain symbols.
|
# Oracle passwords are limited to 30 chars and can't contain symbols.
|
||||||
password = get_random_string(length=30)
|
password = get_random_string(30)
|
||||||
return password
|
return password
|
||||||
|
|
||||||
def _test_database_tblspace(self):
|
def _test_database_tblspace(self):
|
||||||
|
|
|
@ -4,8 +4,10 @@ Django's standard crypto functions and utilities.
|
||||||
import hashlib
|
import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
import secrets
|
import secrets
|
||||||
|
import warnings
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils.deprecation import RemovedInDjango40Warning
|
||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,15 +46,31 @@ def salted_hmac(key_salt, value, secret=None, *, algorithm='sha1'):
|
||||||
return hmac.new(key, msg=force_bytes(value), digestmod=hasher)
|
return hmac.new(key, msg=force_bytes(value), digestmod=hasher)
|
||||||
|
|
||||||
|
|
||||||
def get_random_string(length=12,
|
NOT_PROVIDED = object() # RemovedInDjango40Warning.
|
||||||
allowed_chars='abcdefghijklmnopqrstuvwxyz'
|
|
||||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'):
|
|
||||||
|
# RemovedInDjango40Warning: when the deprecation ends, replace with:
|
||||||
|
# def get_random_string(self, length, allowed_chars='...'):
|
||||||
|
def get_random_string(length=NOT_PROVIDED, allowed_chars=(
|
||||||
|
'abcdefghijklmnopqrstuvwxyz'
|
||||||
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
||||||
|
)):
|
||||||
"""
|
"""
|
||||||
Return a securely generated random string.
|
Return a securely generated random string.
|
||||||
|
|
||||||
The default length of 12 with the a-z, A-Z, 0-9 character set returns
|
The bit length of the returned value can be calculated with the formula:
|
||||||
a 71-bit value. log_2((26+26+10)^12) =~ 71 bits
|
log_2(len(allowed_chars)^length)
|
||||||
|
|
||||||
|
For example, with default `allowed_chars` (26+26+10), this gives:
|
||||||
|
* length: 12, bit length =~ 71 bits
|
||||||
|
* length: 22, bit length =~ 131 bits
|
||||||
"""
|
"""
|
||||||
|
if length is NOT_PROVIDED:
|
||||||
|
warnings.warn(
|
||||||
|
'Not providing a length argument is deprecated.',
|
||||||
|
RemovedInDjango40Warning,
|
||||||
|
)
|
||||||
|
length = 12
|
||||||
return ''.join(secrets.choice(allowed_chars) for i in range(length))
|
return ''.join(secrets.choice(allowed_chars) for i in range(length))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,9 @@ details on these changes.
|
||||||
* The ``providing_args`` argument for ``django.dispatch.Signal`` will be
|
* The ``providing_args`` argument for ``django.dispatch.Signal`` will be
|
||||||
removed.
|
removed.
|
||||||
|
|
||||||
|
* The ``length`` argument for ``django.utils.crypto.get_random_string()`` will
|
||||||
|
be required.
|
||||||
|
|
||||||
See the :ref:`Django 3.1 release notes <deprecated-features-3.1>` for more
|
See the :ref:`Django 3.1 release notes <deprecated-features-3.1>` for more
|
||||||
details on these changes.
|
details on these changes.
|
||||||
|
|
||||||
|
|
|
@ -554,6 +554,9 @@ Miscellaneous
|
||||||
argument as documentation, you can move the text to a code comment or
|
argument as documentation, you can move the text to a code comment or
|
||||||
docstring.
|
docstring.
|
||||||
|
|
||||||
|
* Calling ``django.utils.crypto.get_random_string()`` without a ``length``
|
||||||
|
argument is deprecated.
|
||||||
|
|
||||||
.. _removed-features-3.1:
|
.. _removed-features-3.1:
|
||||||
|
|
||||||
Features removed in 3.1
|
Features removed in 3.1
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import hashlib
|
import hashlib
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, ignore_warnings
|
||||||
from django.utils.crypto import (
|
from django.utils.crypto import (
|
||||||
InvalidAlgorithm, constant_time_compare, pbkdf2, salted_hmac,
|
InvalidAlgorithm, constant_time_compare, get_random_string, pbkdf2,
|
||||||
|
salted_hmac,
|
||||||
)
|
)
|
||||||
|
from django.utils.deprecation import RemovedInDjango40Warning
|
||||||
|
|
||||||
|
|
||||||
class TestUtilsCryptoMisc(SimpleTestCase):
|
class TestUtilsCryptoMisc(SimpleTestCase):
|
||||||
|
@ -183,3 +185,14 @@ class TestUtilsCryptoPBKDF2(unittest.TestCase):
|
||||||
def test_default_hmac_alg(self):
|
def test_default_hmac_alg(self):
|
||||||
kwargs = {'password': b'password', 'salt': b'salt', 'iterations': 1, 'dklen': 20}
|
kwargs = {'password': b'password', 'salt': b'salt', 'iterations': 1, 'dklen': 20}
|
||||||
self.assertEqual(pbkdf2(**kwargs), hashlib.pbkdf2_hmac(hash_name=hashlib.sha256().name, **kwargs))
|
self.assertEqual(pbkdf2(**kwargs), hashlib.pbkdf2_hmac(hash_name=hashlib.sha256().name, **kwargs))
|
||||||
|
|
||||||
|
|
||||||
|
class DeprecationTests(SimpleTestCase):
|
||||||
|
@ignore_warnings(category=RemovedInDjango40Warning)
|
||||||
|
def test_get_random_string(self):
|
||||||
|
self.assertEqual(len(get_random_string()), 12)
|
||||||
|
|
||||||
|
def test_get_random_string_warning(self):
|
||||||
|
msg = 'Not providing a length argument is deprecated.'
|
||||||
|
with self.assertRaisesMessage(RemovedInDjango40Warning, msg):
|
||||||
|
get_random_string()
|
||||||
|
|
Loading…
Reference in New Issue