Another security question

Chris Angelico rosuav at
Fri Dec 23 06:26:17 EST 2016

On Fri, Dec 23, 2016 at 9:19 PM, Frank Millman <frank at> wrote:
> At present I just store a SHA-1 hash of the password for each user. Here are
> my thoughts on improving this.
> 1. Generate a 'salt' for each password. There seem to be two ways in the
> standard library to do this -
>    import os
>    salt = os.urandom(16)
>    import secrets
>    salt = secrets.token_bytes(16)
>    My guess is that it will not make much difference which I use.

The main difference is that the 'secrets' module is new in Python 3.6.
If you use anything older - and there are a lot of 3.5s and 3.4s out
there - you can't use it (unless there's a PyPI backport or
something). So if you need compatibility with older Pythons, use
os.urandom; if you're okay with 3.6+, use secrets.

> 2. Store the salt in the database along with the user-id and hashed password
> for each user.

Yep. I generally work with a single database field containing the salt
and the hash as a combined "encrypted password", as it's convenient to
work that way. It's also often worth storing some kind of signature so
you know what scheme you used; in the future, you will eventually
decide that your passwords aren't really secure enough for long-term,
and you'll want to progressively change over. You can't do that by
decrypting and re-encrypting the passwords (since you can't decrypt
them), so you have to introduce a new scheme while still supporting
the old one. Technically you could detect the scheme by the encrypted
length, but it's easier and safer to have a signature.

> 3. Generate the password from the string supplied by the user as follows -
>    from hashlib import blake2b
>    password = blake2b('my_password'.encode('utf-8'), salt=salt).digest()
> The hashlib docs have the following warning -
> "Salted hashing (or just hashing) with BLAKE2 or any other general-purpose
> cryptographic hash function, such as SHA-256, is not suitable for hashing
> passwords. See BLAKE2 FAQ for more information."
> I propose to ignore this warning. I feel that, for my purposes, the above
> procedure is adequate.
> Does all this sound reasonable?

Check out some prior art. When I build a web app using Flask, I
generally use Werkzeug's password management features:

As well as doing everything I said above about salting and hashing and
having signatures, it pushes the responsibility onto someone else. You
just give it a password and get back an ASCII string that you stash in
the database. If there's a security flaw, Werkzeug can push a new
version that fixes it - it's not your problem.

At very least, be aware of what these kinds of libraries are doing.
I'm not saying you should blindly trust them or automatically reach
for a dependency, but they're worth looking at.


More information about the Python-list mailing list