Skip to content

Commit eb334a1

Browse files
committed
issue2551253. default hash is PBKDF2-SHA512.
The default password hashing algorithm has been upgraded to PBKDF2-SHA512 from PBKDF2-SHA1. The default pbkdf2 rounds in the config file has been changed to 250000. Doc updated.
1 parent 648e3d0 commit eb334a1

File tree

4 files changed

+65
-7
lines changed

4 files changed

+65
-7
lines changed

CHANGES.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ Fixed:
4949
- issue2551383 - Setting same address via REST PUT command results in
5050
an error. Now the userauditor does not trigger an error if a user
5151
sets the primary address to the existing value. (John Rouillard)
52+
- issue2551253 - Modify password PBKDF2 method to use SHA512. The
53+
default password hashing algorithm has been upgraded to
54+
PBKDF2-SHA512 from PBKDF2-SHA1. The default pbkdf2 rounds in the
55+
config file has been changed to 250000. The admin should change it
56+
manually if it is at 2 million. PBKDF2-SHA512 (PBKDF2S5) has been
57+
available since release 2.3, but it required a manual step to make
58+
it the default. (John Rouillard)
5259

5360
Features:
5461

doc/upgrading.txt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,41 @@ following diff::
159159
add the lines marked with ``+`` in the file in the location after
160160
check_main is assigned.
161161

162+
Modify config.ini password_pbkdf2_default_rounds setting (recommended)
163+
----------------------------------------------------------------------
164+
165+
The method for hashing and storing passwords has been updated to use
166+
PBKDF2 with SHA512 hash. This change was first introduced in Roundup
167+
2.3 and is now the standard. If you previously added code in
168+
interfaces.py for a `PBKDF2 upgrade`_ to enable PBKDF2S5, you can
169+
remove that code now.
170+
171+
SHA512 is a more secure hash, it requires fewer rounds to ensure
172+
safety. The older PBKDF2-SHA1 needed around 2 million rounds.
173+
174+
You should update the ``password_pbkdf2_default_rounds`` setting in
175+
``config.ini`` to 250000. This value is higher than the OWASP
176+
recommendation of 210000 from three years ago. If you don’t make this
177+
change, logins will be slow, especially for REST or XMLRPC calls.
178+
179+
See `PBKDF2 upgrade`_ for details on how to test the algorithm's
180+
speed. We do not recommend reverting to the older SHA1 PBKDF2. If you
181+
have to do so due to a slow CPU, you can add the following to your
182+
tracker's ``interfaces.py``::
183+
184+
from roundup.password import Password
185+
## Use PBDKF2 (PBKDF2-SHA1) as default hash for passwords.
186+
# That scheme is at the start of the deprecated_schemes list and ha
187+
# to be removed.
188+
Password.default_scheme = Password.deprecated_schemes.pop(0)
189+
# Add PBKDF2S5 (PBKDF2-SHA512) as a valid scheme. Passwords
190+
# using it will be rehashed to use PBDKF2.
191+
Password.experimental_schemes[0] = "PBKDF2S5"
192+
193+
If you proceed with this, you should set
194+
``password_pbkdf2_default_rounds`` to 2 million or more rounds to keep
195+
your hashed password database secure in case it gets stolen.
196+
162197
Defusedxml support improves XMLRPC security (optional)
163198
------------------------------------------------------
164199

@@ -1292,6 +1327,8 @@ install an OS vendor package or some other library.
12921327

12931328
.. _recommended setting of 1,300,000: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
12941329

1330+
.. _PBKDF2 upgrade:
1331+
12951332
Upgrade to PBKDF2-SHA512 from current PBKDF2-SHA1 (recommended)
12961333
---------------------------------------------------------------
12971334

roundup/configuration.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,10 +1139,11 @@ def str2value(self, value):
11391139
"get the error 'Error: field larger than field limit' during\n"
11401140
"import."),
11411141
(IntegerNumberGeqZeroOption, 'password_pbkdf2_default_rounds',
1142-
'2000000',
1142+
'250000',
11431143
"Sets the default number of rounds used when encoding passwords\n"
1144-
"using the PBKDF2 scheme. Set this to a higher value on faster\n"
1145-
"systems which want more security.\n"
1144+
"using any PBKDF2 scheme. Set this to a higher value on faster\n"
1145+
"systems which want more security. Use a minimum of 250000\n"
1146+
"for PBKDF2-SHA512 which is the default hash in Roundup 2.5.\n"
11461147
"PBKDF2 (Password-Based Key Derivation Function) is a\n"
11471148
"password hashing mechanism that derives hash from the\n"
11481149
"password and a random salt. For authentication this process\n"

roundup/password.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ class JournalPassword:
331331
version only reads the encryption scheme from the given
332332
encrypted password.
333333
"""
334-
default_scheme = 'PBKDF2' # new encryptions use this scheme
334+
default_scheme = 'PBKDF2S5' # new encryptions use this scheme
335335
pwre = re.compile(r'{(\w+)}(.+)')
336336

337337
def __init__(self, encrypted=''):
@@ -394,12 +394,12 @@ class Password(JournalPassword):
394394
1
395395
"""
396396

397-
deprecated_schemes = ["SSHA", "SHA", "MD5", "plaintext"]
397+
deprecated_schemes = ["PBKDF2", "SSHA", "SHA", "MD5", "plaintext"]
398398
if crypt:
399399
# place just before plaintext if crypt is available
400400
deprecated_schemes.insert(-1, "crypt")
401-
experimental_schemes = ["PBKDF2S5"]
402-
known_schemes = ["PBKDF2"] + experimental_schemes + \
401+
experimental_schemes = []
402+
known_schemes = ["PBKDF2S5"] + experimental_schemes + \
403403
deprecated_schemes
404404

405405
def __init__(self, plaintext=None, scheme=None, encrypted=None,
@@ -442,6 +442,19 @@ def needs_migration(self, config):
442442
new_rounds = 1000
443443
if rounds < int(new_rounds):
444444
return True
445+
446+
if (self.scheme == "PBKDF2S5"):
447+
new_rounds = config.PASSWORD_PBKDF2_DEFAULT_ROUNDS
448+
if ("pytest" in sys.modules and
449+
"PYTEST_CURRENT_TEST" in os.environ):
450+
if ("PYTEST_USE_CONFIG" in os.environ):
451+
new_rounds = config.PASSWORD_PBKDF2_DEFAULT_ROUNDS
452+
else:
453+
# for testing
454+
new_rounds = 1000
455+
if rounds < int(new_rounds):
456+
return True
457+
445458
return False
446459

447460
def unpack(self, encrypted, scheme=None, strict=False, config=None):

0 commit comments

Comments
 (0)