Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Revert "[1.4.x] Ensure that passwords are never long enough for a DoS."

This reverts commit 3f3d887.

This fix is no longer necessary, our pbkdf2 (see next commit) implementation
no longer rehashes the password every iteration.
  • Loading branch information...
commit 0317edf0c7779902d49c6efb8242af61e5569cde 1 parent ca77e38
Florian Apolloner authored September 24, 2013
51  django/contrib/auth/forms.py
@@ -8,10 +8,7 @@
8 8
 
9 9
 from django.contrib.auth import authenticate
10 10
 from django.contrib.auth.models import User
11  
-from django.contrib.auth.hashers import (
12  
-    MAXIMUM_PASSWORD_LENGTH, UNUSABLE_PASSWORD,
13  
-    is_password_usable, get_hasher
14  
-)
  11
+from django.contrib.auth.hashers import UNUSABLE_PASSWORD, is_password_usable, get_hasher
15 12
 from django.contrib.auth.tokens import default_token_generator
16 13
 from django.contrib.sites.models import get_current_site
17 14
 
@@ -73,11 +70,10 @@ class UserCreationForm(forms.ModelForm):
73 70
             'invalid': _("This value may contain only letters, numbers and "
74 71
                          "@/./+/-/_ characters.")})
75 72
     password1 = forms.CharField(label=_("Password"),
76  
-        widget=forms.PasswordInput, max_length=MAXIMUM_PASSWORD_LENGTH)
  73
+        widget=forms.PasswordInput)
77 74
     password2 = forms.CharField(label=_("Password confirmation"),
78 75
         widget=forms.PasswordInput,
79  
-        max_length=MAXIMUM_PASSWORD_LENGTH,
80  
-        help_text=_("Enter the same password as above, for verification."))
  76
+        help_text = _("Enter the same password as above, for verification."))
81 77
 
82 78
     class Meta:
83 79
         model = User
@@ -141,11 +137,7 @@ class AuthenticationForm(forms.Form):
141 137
     username/password logins.
142 138
     """
143 139
     username = forms.CharField(label=_("Username"), max_length=30)
144  
-    password = forms.CharField(
145  
-        label=_("Password"),
146  
-        widget=forms.PasswordInput,
147  
-        max_length=MAXIMUM_PASSWORD_LENGTH,
148  
-    )
  140
+    password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
149 141
 
150 142
     error_messages = {
151 143
         'invalid_login': _("Please enter a correct username and password. "
@@ -258,16 +250,10 @@ class SetPasswordForm(forms.Form):
258 250
     error_messages = {
259 251
         'password_mismatch': _("The two password fields didn't match."),
260 252
     }
261  
-    new_password1 = forms.CharField(
262  
-        label=_("New password"),
263  
-        widget=forms.PasswordInput,
264  
-        max_length=MAXIMUM_PASSWORD_LENGTH,
265  
-    )
266  
-    new_password2 = forms.CharField(
267  
-        label=_("New password confirmation"),
268  
-        widget=forms.PasswordInput,
269  
-        max_length=MAXIMUM_PASSWORD_LENGTH,
270  
-    )
  253
+    new_password1 = forms.CharField(label=_("New password"),
  254
+                                    widget=forms.PasswordInput)
  255
+    new_password2 = forms.CharField(label=_("New password confirmation"),
  256
+                                    widget=forms.PasswordInput)
271 257
 
272 258
     def __init__(self, user, *args, **kwargs):
273 259
         self.user = user
@@ -298,11 +284,8 @@ class PasswordChangeForm(SetPasswordForm):
298 284
         'password_incorrect': _("Your old password was entered incorrectly. "
299 285
                                 "Please enter it again."),
300 286
     })
301  
-    old_password = forms.CharField(
302  
-        label=_("Old password"),
303  
-        widget=forms.PasswordInput,
304  
-        max_length=MAXIMUM_PASSWORD_LENGTH,
305  
-    )
  287
+    old_password = forms.CharField(label=_("Old password"),
  288
+                                   widget=forms.PasswordInput)
306 289
 
307 290
     def clean_old_password(self):
308 291
         """
@@ -324,16 +307,10 @@ class AdminPasswordChangeForm(forms.Form):
324 307
     error_messages = {
325 308
         'password_mismatch': _("The two password fields didn't match."),
326 309
     }
327  
-    password1 = forms.CharField(
328  
-        label=_("Password"),
329  
-        widget=forms.PasswordInput,
330  
-        max_length=MAXIMUM_PASSWORD_LENGTH,
331  
-    )
332  
-    password2 = forms.CharField(
333  
-        label=_("Password (again)"),
334  
-        widget=forms.PasswordInput,
335  
-        max_length=MAXIMUM_PASSWORD_LENGTH,
336  
-    )
  310
+    password1 = forms.CharField(label=_("Password"),
  311
+                                widget=forms.PasswordInput)
  312
+    password2 = forms.CharField(label=_("Password (again)"),
  313
+                                widget=forms.PasswordInput)
337 314
 
338 315
     def __init__(self, user, *args, **kwargs):
339 316
         self.user = user
29  django/contrib/auth/hashers.py
... ...
@@ -1,4 +1,3 @@
1  
-import functools
2 1
 import hashlib
3 2
 
4 3
 from django.conf import settings
@@ -12,23 +11,10 @@
12 11
 
13 12
 
14 13
 UNUSABLE_PASSWORD = '!'  # This will never be a valid encoded hash
15  
-MAXIMUM_PASSWORD_LENGTH = 4096  # The maximum length a password can be to prevent DoS
16 14
 HASHERS = None  # lazily loaded from PASSWORD_HASHERS
17 15
 PREFERRED_HASHER = None  # defaults to first item in PASSWORD_HASHERS
18 16
 
19 17
 
20  
-def password_max_length(max_length):
21  
-    def inner(fn):
22  
-        @functools.wraps(fn)
23  
-        def wrapper(self, password, *args, **kwargs):
24  
-            if len(password) > max_length:
25  
-                raise ValueError("Invalid password; Must be less than or equal"
26  
-                                 " to %d bytes" % max_length)
27  
-            return fn(self, password, *args, **kwargs)
28  
-        return wrapper
29  
-    return inner
30  
-
31  
-
32 18
 def is_password_usable(encoded):
33 19
     return (encoded is not None and encoded != UNUSABLE_PASSWORD)
34 20
 
@@ -216,7 +202,6 @@ class PBKDF2PasswordHasher(BasePasswordHasher):
216 202
     iterations = 10000
217 203
     digest = hashlib.sha256
218 204
 
219  
-    @password_max_length(MAXIMUM_PASSWORD_LENGTH)
220 205
     def encode(self, password, salt, iterations=None):
221 206
         assert password
222 207
         assert salt and '$' not in salt
@@ -226,7 +211,6 @@ def encode(self, password, salt, iterations=None):
226 211
         hash = hash.encode('base64').strip()
227 212
         return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)
228 213
 
229  
-    @password_max_length(MAXIMUM_PASSWORD_LENGTH)
230 214
     def verify(self, password, encoded):
231 215
         algorithm, iterations, salt, hash = encoded.split('$', 3)
232 216
         assert algorithm == self.algorithm
@@ -272,13 +256,11 @@ def salt(self):
272 256
         bcrypt = self._load_library()
273 257
         return bcrypt.gensalt(self.rounds)
274 258
 
275  
-    @password_max_length(MAXIMUM_PASSWORD_LENGTH)
276 259
     def encode(self, password, salt):
277 260
         bcrypt = self._load_library()
278 261
         data = bcrypt.hashpw(password, salt)
279 262
         return "%s$%s" % (self.algorithm, data)
280 263
 
281  
-    @password_max_length(MAXIMUM_PASSWORD_LENGTH)
282 264
     def verify(self, password, encoded):
283 265
         algorithm, data = encoded.split('$', 1)
284 266
         assert algorithm == self.algorithm
@@ -303,14 +285,12 @@ class SHA1PasswordHasher(BasePasswordHasher):
303 285
     """
304 286
     algorithm = "sha1"
305 287
 
306  
-    @password_max_length(MAXIMUM_PASSWORD_LENGTH)
307 288
     def encode(self, password, salt):
308 289
         assert password
309 290
         assert salt and '$' not in salt
310 291
         hash = hashlib.sha1(salt + password).hexdigest()
311 292
         return "%s$%s$%s" % (self.algorithm, salt, hash)
312 293
 
313  
-    @password_max_length(MAXIMUM_PASSWORD_LENGTH)
314 294
     def verify(self, password, encoded):
315 295
         algorithm, salt, hash = encoded.split('$', 2)
316 296
         assert algorithm == self.algorithm
@@ -333,14 +313,12 @@ class MD5PasswordHasher(BasePasswordHasher):
333 313
     """
334 314
     algorithm = "md5"
335 315
 
336  
-    @password_max_length(MAXIMUM_PASSWORD_LENGTH)
337 316
     def encode(self, password, salt):
338 317
         assert password
339 318
         assert salt and '$' not in salt
340 319
         hash = hashlib.md5(salt + password).hexdigest()
341 320
         return "%s$%s$%s" % (self.algorithm, salt, hash)
342 321
 
343  
-    @password_max_length(MAXIMUM_PASSWORD_LENGTH)
344 322
     def verify(self, password, encoded):
345 323
         algorithm, salt, hash = encoded.split('$', 2)
346 324
         assert algorithm == self.algorithm
@@ -371,13 +349,11 @@ class UnsaltedSHA1PasswordHasher(BasePasswordHasher):
371 349
     def salt(self):
372 350
         return ''
373 351
 
374  
-    @password_max_length(MAXIMUM_PASSWORD_LENGTH)
375 352
     def encode(self, password, salt):
376 353
         assert salt == ''
377 354
         hash = hashlib.sha1(password).hexdigest()
378 355
         return 'sha1$$%s' % hash
379 356
 
380  
-    @password_max_length(MAXIMUM_PASSWORD_LENGTH)
381 357
     def verify(self, password, encoded):
382 358
         encoded_2 = self.encode(password, '')
383 359
         return constant_time_compare(encoded, encoded_2)
@@ -407,12 +383,10 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher):
407 383
     def salt(self):
408 384
         return ''
409 385
 
410  
-    @password_max_length(MAXIMUM_PASSWORD_LENGTH)
411 386
     def encode(self, password, salt):
412 387
         assert salt == ''
413 388
         return hashlib.md5(password).hexdigest()
414 389
 
415  
-    @password_max_length(MAXIMUM_PASSWORD_LENGTH)
416 390
     def verify(self, password, encoded):
417 391
         if len(encoded) == 37 and encoded.startswith('md5$$'):
418 392
             encoded = encoded[5:]
@@ -438,7 +412,6 @@ class CryptPasswordHasher(BasePasswordHasher):
438 412
     def salt(self):
439 413
         return get_random_string(2)
440 414
 
441  
-    @password_max_length(MAXIMUM_PASSWORD_LENGTH)
442 415
     def encode(self, password, salt):
443 416
         crypt = self._load_library()
444 417
         assert len(salt) == 2
@@ -446,7 +419,6 @@ def encode(self, password, salt):
446 419
         # we don't need to store the salt, but Django used to do this
447 420
         return "%s$%s$%s" % (self.algorithm, '', data)
448 421
 
449  
-    @password_max_length(MAXIMUM_PASSWORD_LENGTH)
450 422
     def verify(self, password, encoded):
451 423
         crypt = self._load_library()
452 424
         algorithm, salt, data = encoded.split('$', 2)
@@ -461,3 +433,4 @@ def safe_summary(self, encoded):
461 433
             (_('salt'), salt),
462 434
             (_('hash'), mask_hash(data, show=3)),
463 435
         ])
  436
+
72  django/contrib/auth/tests/hashers.py
... ...
@@ -1,8 +1,7 @@
1 1
 from django.conf.global_settings import PASSWORD_HASHERS as default_hashers
2 2
 from django.contrib.auth.hashers import (is_password_usable,
3 3
     check_password, make_password, PBKDF2PasswordHasher, load_hashers,
4  
-    PBKDF2SHA1PasswordHasher, get_hasher, UNUSABLE_PASSWORD,
5  
-    MAXIMUM_PASSWORD_LENGTH, password_max_length)
  4
+    PBKDF2SHA1PasswordHasher, get_hasher, UNUSABLE_PASSWORD)
6 5
 from django.utils import unittest
7 6
 from django.utils.unittest import skipUnless
8 7
 from django.test.utils import override_settings
@@ -29,12 +28,6 @@ def test_simple(self):
29 28
         self.assertTrue(is_password_usable(encoded))
30 29
         self.assertTrue(check_password(u'letmein', encoded))
31 30
         self.assertFalse(check_password('letmeinz', encoded))
32  
-        # Long password
33  
-        self.assertRaises(
34  
-            ValueError,
35  
-            make_password,
36  
-            "1" * (MAXIMUM_PASSWORD_LENGTH + 1),
37  
-        )
38 31
 
39 32
     def test_pkbdf2(self):
40 33
         encoded = make_password('letmein', 'seasalt', 'pbkdf2_sha256')
@@ -43,14 +36,6 @@ def test_pkbdf2(self):
43 36
         self.assertTrue(is_password_usable(encoded))
44 37
         self.assertTrue(check_password(u'letmein', encoded))
45 38
         self.assertFalse(check_password('letmeinz', encoded))
46  
-        # Long password
47  
-        self.assertRaises(
48  
-            ValueError,
49  
-            make_password,
50  
-            "1" * (MAXIMUM_PASSWORD_LENGTH + 1),
51  
-            "seasalt",
52  
-            "pbkdf2_sha256",
53  
-        )
54 39
 
55 40
     def test_sha1(self):
56 41
         encoded = make_password('letmein', 'seasalt', 'sha1')
@@ -59,14 +44,6 @@ def test_sha1(self):
59 44
         self.assertTrue(is_password_usable(encoded))
60 45
         self.assertTrue(check_password(u'letmein', encoded))
61 46
         self.assertFalse(check_password('letmeinz', encoded))
62  
-        # Long password
63  
-        self.assertRaises(
64  
-            ValueError,
65  
-            make_password,
66  
-            "1" * (MAXIMUM_PASSWORD_LENGTH + 1),
67  
-            "seasalt",
68  
-            "sha1",
69  
-        )
70 47
 
71 48
     def test_md5(self):
72 49
         encoded = make_password('letmein', 'seasalt', 'md5')
@@ -75,14 +52,6 @@ def test_md5(self):
75 52
         self.assertTrue(is_password_usable(encoded))
76 53
         self.assertTrue(check_password(u'letmein', encoded))
77 54
         self.assertFalse(check_password('letmeinz', encoded))
78  
-        # Long password
79  
-        self.assertRaises(
80  
-            ValueError,
81  
-            make_password,
82  
-            "1" * (MAXIMUM_PASSWORD_LENGTH + 1),
83  
-            "seasalt",
84  
-            "md5",
85  
-        )
86 55
 
87 56
     def test_unsalted_md5(self):
88 57
         encoded = make_password('letmein', '', 'unsalted_md5')
@@ -95,14 +64,6 @@ def test_unsalted_md5(self):
95 64
         self.assertTrue(is_password_usable(alt_encoded))
96 65
         self.assertTrue(check_password(u'letmein', alt_encoded))
97 66
         self.assertFalse(check_password('letmeinz', alt_encoded))
98  
-        # Long password
99  
-        self.assertRaises(
100  
-            ValueError,
101  
-            make_password,
102  
-            "1" * (MAXIMUM_PASSWORD_LENGTH + 1),
103  
-            "",
104  
-            "unsalted_md5",
105  
-        )
106 67
 
107 68
     def test_unsalted_sha1(self):
108 69
         encoded = make_password('letmein', '', 'unsalted_sha1')
@@ -113,14 +74,6 @@ def test_unsalted_sha1(self):
113 74
         # Raw SHA1 isn't acceptable
114 75
         alt_encoded = encoded[6:]
115 76
         self.assertRaises(ValueError, check_password, 'letmein', alt_encoded)
116  
-        # Long password
117  
-        self.assertRaises(
118  
-            ValueError,
119  
-            make_password,
120  
-            "1" * (MAXIMUM_PASSWORD_LENGTH + 1),
121  
-            "",
122  
-            "unslated_sha1",
123  
-        )
124 77
 
125 78
     @skipUnless(crypt, "no crypt module to generate password.")
126 79
     def test_crypt(self):
@@ -129,14 +82,6 @@ def test_crypt(self):
129 82
         self.assertTrue(is_password_usable(encoded))
130 83
         self.assertTrue(check_password(u'letmein', encoded))
131 84
         self.assertFalse(check_password('letmeinz', encoded))
132  
-        # Long password
133  
-        self.assertRaises(
134  
-            ValueError,
135  
-            make_password,
136  
-            "1" * (MAXIMUM_PASSWORD_LENGTH + 1),
137  
-            "seasalt",
138  
-            "crypt",
139  
-        )
140 85
 
141 86
     @skipUnless(bcrypt, "py-bcrypt not installed")
142 87
     def test_bcrypt(self):
@@ -145,13 +90,6 @@ def test_bcrypt(self):
145 90
         self.assertTrue(encoded.startswith('bcrypt$'))
146 91
         self.assertTrue(check_password(u'letmein', encoded))
147 92
         self.assertFalse(check_password('letmeinz', encoded))
148  
-        # Long password
149  
-        self.assertRaises(
150  
-            ValueError,
151  
-            make_password,
152  
-            "1" * (MAXIMUM_PASSWORD_LENGTH + 1),
153  
-            hasher="bcrypt",
154  
-        )
155 93
 
156 94
     def test_unusable(self):
157 95
         encoded = make_password(None)
@@ -167,14 +105,6 @@ def doit():
167 105
             make_password('letmein', hasher='lolcat')
168 106
         self.assertRaises(ValueError, doit)
169 107
 
170  
-    def test_max_password_length_decorator(self):
171  
-        @password_max_length(10)
172  
-        def encode(s, password, salt):
173  
-            return True
174  
-
175  
-        self.assertTrue(encode(None, "1234", "1234"))
176  
-        self.assertRaises(ValueError, encode, None, "1234567890A", "1234")
177  
-
178 108
     def test_low_level_pkbdf2(self):
179 109
         hasher = PBKDF2PasswordHasher()
180 110
         encoded = hasher.encode('letmein', 'seasalt')

0 notes on commit 0317edf

Please sign in to comment.
Something went wrong with that request. Please try again.