Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved PBKDF2 defaults #753

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 27 additions & 17 deletions tests/test_security.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def test_safe_str_cmp():
def test_password_hashing():
hash0 = generate_password_hash('default')
assert check_password_hash(hash0, 'default')
assert hash0.startswith('pbkdf2:sha1:1000$')
assert hash0.startswith('pbkdf2:sha256:50000$')

hash1 = generate_password_hash('default', 'sha1')
hash2 = generate_password_hash(u'default', method='sha1')
Expand Down Expand Up @@ -59,40 +59,50 @@ def test_safe_join():


def test_pbkdf2():
def check(data, salt, iterations, keylen, expected):
rv = pbkdf2_hex(data, salt, iterations, keylen)
def check(data, salt, iterations, keylen, hashfunc, expected):
rv = pbkdf2_hex(data, salt, iterations, keylen, hashfunc)
assert rv == expected

# From RFC 6070
check('password', 'salt', 1, None,
'0c60c80f961f0e71f3a9b524af6012062fe037a6')
check('password', 'salt', 1, 20,

# Assumes default keylen is 20
# check('password', 'salt', 1, None,
# '0c60c80f961f0e71f3a9b524af6012062fe037a6')
check('password', 'salt', 1, 20, 'sha1',
'0c60c80f961f0e71f3a9b524af6012062fe037a6')
check('password', 'salt', 2, 20,
check('password', 'salt', 2, 20, 'sha1',
'ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957')
check('password', 'salt', 4096, 20,
check('password', 'salt', 4096, 20, 'sha1',
'4b007901b765489abead49d926f721d065a429c1')
check('passwordPASSWORDpassword', 'saltSALTsaltSALTsaltSALTsaltSALTsalt',
4096, 25, '3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038')
check('pass\x00word', 'sa\x00lt', 4096, 16,
4096, 25, 'sha1', '3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038')
check('pass\x00word', 'sa\x00lt', 4096, 16, 'sha1',
'56fa6aa75548099dcc37d7f03425e0c3')

# PBKDF2-HMAC-SHA256 test vectors
check('password', 'salt', 1, 32, 'sha256',
'120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b')
check('password', 'salt', 2, 32, 'sha256',
'ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43')
check('password', 'salt', 4096, 20, 'sha256',
'c5e478d59288c841aa530db6845c4c8d962893a0')

# This one is from the RFC but it just takes for ages
# check('password', 'salt', 16777216, 20,
# 'eefe3d61cd4da4e4e9945b3d6ba2158c2634e984')

# From Crypt-PBKDF2
check('password', 'ATHENA.MIT.EDUraeburn', 1, 16,
check('password', 'ATHENA.MIT.EDUraeburn', 1, 16, 'sha1',
'cdedb5281bb2f801565a1122b2563515')
check('password', 'ATHENA.MIT.EDUraeburn', 1, 32,
check('password', 'ATHENA.MIT.EDUraeburn', 1, 32, 'sha1',
'cdedb5281bb2f801565a1122b25635150ad1f7a04bb9f3a333ecc0e2e1f70837')
check('password', 'ATHENA.MIT.EDUraeburn', 2, 16,
check('password', 'ATHENA.MIT.EDUraeburn', 2, 16, 'sha1',
'01dbee7f4a9e243e988b62c73cda935d')
check('password', 'ATHENA.MIT.EDUraeburn', 2, 32,
check('password', 'ATHENA.MIT.EDUraeburn', 2, 32, 'sha1',
'01dbee7f4a9e243e988b62c73cda935da05378b93244ec8f48a99e61ad799d86')
check('password', 'ATHENA.MIT.EDUraeburn', 1200, 32,
check('password', 'ATHENA.MIT.EDUraeburn', 1200, 32, 'sha1',
'5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13')
check('X' * 64, 'pass phrase equals block size', 1200, 32,
check('X' * 64, 'pass phrase equals block size', 1200, 32, 'sha1',
'139c30c0966bc32ba55fdbf212530ac9c5ec59f1a452f5cc9ad940fea0598ed1')
check('X' * 65, 'pass phrase exceeds block size', 1200, 32,
check('X' * 65, 'pass phrase exceeds block size', 1200, 32, 'sha1',
'9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a')
16 changes: 8 additions & 8 deletions werkzeug/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@


SALT_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
DEFAULT_PBKDF2_ITERATIONS = 1000
DEFAULT_PBKDF2_ITERATIONS = 50000


_pack_int = Struct('>I').pack
Expand Down Expand Up @@ -59,7 +59,7 @@ def pbkdf2_hex(data, salt, iterations=DEFAULT_PBKDF2_ITERATIONS,
the digest size will be used.
:param hashfunc: the hash function to use. This can either be the
string name of a known hash function, or a function
from the hashlib module. Defaults to sha1.
from the hashlib module. Defaults to sha256.
"""
rv = pbkdf2_bin(data, salt, iterations, keylen, hashfunc)
return to_native(codecs.encode(rv, 'hex_codec'))
Expand All @@ -72,7 +72,7 @@ def pbkdf2_bin(data, salt, iterations=DEFAULT_PBKDF2_ITERATIONS,
keylen=None, hashfunc=None):
"""Returns a binary digest for the PBKDF2 hash algorithm of `data`
with the given `salt`. It iterates `iterations` times and produces a
key of `keylen` bytes. By default, SHA-1 is used as hash function;
key of `keylen` bytes. By default, SHA-256 is used as hash function;
a different hashlib `hashfunc` can be provided.

.. versionadded:: 0.9
Expand All @@ -84,12 +84,12 @@ def pbkdf2_bin(data, salt, iterations=DEFAULT_PBKDF2_ITERATIONS,
the digest size will be used.
:param hashfunc: the hash function to use. This can either be the
string name of a known hash function or a function
from the hashlib module. Defaults to sha1.
from the hashlib module. Defaults to sha256.
"""
if isinstance(hashfunc, string_types):
hashfunc = _hash_funcs[hashfunc]
elif not hashfunc:
hashfunc = hashlib.sha1
hashfunc = hashlib.sha256
data = to_bytes(data)
salt = to_bytes(salt)

Expand Down Expand Up @@ -201,7 +201,7 @@ def _hash_internal(method, salt, password):
return rv, actual_method


def generate_password_hash(password, method='pbkdf2:sha1', salt_length=8):
def generate_password_hash(password, method='pbkdf2:sha256', salt_length=8):
"""Hash a password with the given method and salt with with a string of
the given length. The format of the string returned includes the method
that was used so that :func:`check_password_hash` can check the hash.
Expand All @@ -217,8 +217,8 @@ def generate_password_hash(password, method='pbkdf2:sha1', salt_length=8):
If PBKDF2 is wanted it can be enabled by setting the method to
``pbkdf2:method:iterations`` where iterations is optional::

pbkdf2:sha1:2000$salt$hash
pbkdf2:sha1$salt$hash
pbkdf2:sha256:80000$salt$hash
pbkdf2:sha256$salt$hash

:param password: the password to hash.
:param method: the hash method to use (one that hashlib supports). Can
Expand Down