flufl.password - Password hashing and verification¶
This package is called flufl.password. It provides for hashing and verification of passwords.
Requirements¶
flufl.password requires Python 2.6 or newer, and is compatible with Python 3.
Documentation¶
A simple guide to using the library is available within this package, in the form of doctests.
Project details¶
- Project home: https://gitlab.com/warsaw/flufl.password
- Report bugs at: https://gitlab.com/warsaw/flufl.password/issues
- Code hosting: git@gitlab.com:warsaw/flufl.password.git
- Documentation: http://fluflpassword.readthedocs.org/
You can install it with pip:
% pip install flufl.password
You can grab the latest development copy of the code using git. The master repository is hosted on GitLab. If you have git installed, you can grab your own branch of the code like this:
$ git clone git@gitlab.com:warsaw/flufl.password.git
You may contact the author via barry@python.org.
Copyright¶
Copyright (C) 2011-2015 Barry A. Warsaw
This file is part of flufl.password.
flufl.password is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
flufl.password is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with flufl.password. If not, see <http://www.gnu.org/licenses/>.
Table of Contents¶
Using the flufl.password package¶
This package comes with a number of password hashing schemes. Some are more
secure, while others provide for useful debugging. A hashed password follows
the syntax promoted in RFC 2307 (as best I can tell), having a basic format
of {scheme}hashed_password
.
Hashing a password¶
You can create a secure hashed password using the default scheme, which includes random data.
>>> from flufl.password import make_secret
>>> show(make_secret('my password'))
{SSHA}...
You can also create a hashed password using one of the other built-in schemes.
>>> from flufl.password.schemes import SHAPasswordScheme
>>> show(make_secret('my password', SHAPasswordScheme))
{SHA}ovj3-hlaCAoipokEHaqPIET58zY=
Available schemes¶
There are several built-in schemes to choose from, which run the gamut from useful for debugging to variously higher levels of security.
The no password scheme throws away the password and always returns the empty string, but with a properly formatted password.
>>> from flufl.password.schemes import NoPasswordScheme >>> show(make_secret('my password', NoPasswordScheme)) {NONE}The clear text scheme returns the original password in clear text, but properly formatted.
>>> from flufl.password.schemes import ClearTextPasswordScheme >>> show(make_secret('my password', ClearTextPasswordScheme)) {CLEARTEXT}my passwordThe SHA1 password scheme encodes the SHA1 hash of the password.
>>> show(make_secret('my password', SHAPasswordScheme)) {SHA}ovj3-hlaCAoipokEHaqPIET58zY=The salted SHA1 scheme adds a random salt to the password’s digest.
>>> from flufl.password.schemes import SSHAPasswordScheme >>> show(make_secret('my password', SSHAPasswordScheme)) {SSHA}...There is an RFC 2898 password encoding scheme.
>>> from flufl.password.schemes import PBKDF2PasswordScheme >>> show(make_secret('my password', PBKDF2PasswordScheme)) {PBKDF2 SHA 2000}...
Custom schemes¶
It’s also easy enough to create your own scheme. It must implement a static make_secret() method, which you can inherit from a common base class. The class must also have a TAG attribute which gives the unique name of this hashing scheme.
The scheme should be registered so that it can be found by its tag for verification purposes. This can be done using the @register descriptor.
>>> from codecs import getencoder
>>> from flufl.password import register
>>> from flufl.password.schemes import PasswordScheme
>>> @register
... class MyScheme(PasswordScheme):
... TAG = 'CAESAR'
... @staticmethod
... def make_secret(password):
... # In Python 3, this is a string-to-string encoding. The
... # caller already turned `password` into a byte string, so
... # we have to pass it back through a string to rotate it.
... # We also can't just call .encode('rot_13') on the string
... # because Python 3.2 chokes on the returned string (it expects
... # a bytes object to be returned). Sigh.
... as_string = password.decode('utf-8')
... encoder = getencoder('rot_13')
... return encoder(as_string)[0].encode('utf-8')
>>> show(make_secret('my password', MyScheme))
{CAESAR}zl cnffjbeq
Hashed passwords are always bytes.
>>> isinstance(make_secret('my password', MyScheme), bytes)
True
Verifying a password¶
When the user entered their original password, you hashed it using one of the schemes mentioned above. You are only storing this hashed password in your database.
The user now wants to log in, so she provides you with her plain text password. You want to see if they match.
The easiest way to do this is to give both the plain text password the user just typed, and the hash password you have in your database.
>>> from flufl.password import verify
>>> verify(b'{SHA}ovj3-hlaCAoipokEHaqPIET58zY=', 'my password')
True
Of course, if they enter the wrong password, it does not verify.
>>> verify(b'{SHA}ovj3-hlaCAoipokEHaqPIET58zY=', 'your password')
False
Your custom hashing scheme must implement the check_response() API in order to support password verification. The PasswordScheme base class supports the most obvious implementation of this, which serves for most schemes. For example, the Caesar scheme does not need to implement a check_response() method.
>>> verify(b'{CAESAR}zl cnffjbeq', 'my password')
True
User-friendly passwords¶
This package also provides a convenient utility for generating user friendly passwords. These passwords gather random input and translate them into pairs of vowel-consonant (or consonant-vowel) syllables. It then strings together enough of these syllables to match the requested password length. In theory, this produces relatively secure passwords that are easier to pronounce and remember. The security claims of these generated passwords have not been evaluated.
>>> from flufl.password import generate
>>> my_password = generate(10)
>>> len(my_password)
10
>>> sum(1 for c in my_password if c in 'aeiou')
5
>>> sum(1 for c in my_password if c not in 'aeiou')
5
NEWS for flufl.password¶
1.4 (201X-XX-XX)¶
1.3 (2014-09-24)¶
- Fix documentation bug. (LP: #1026403)
- Purge all references to distribute.
- Describe the switch to git and the repository move.
1.2.1 (2012-04-19)¶
- Add classifiers to setup.py and make the long description more compatible with the Cheeseshop.
- Other changes to make the Cheeseshop page look nicer. (LP: #680136)
- setup_helper.py version 2.1.
1.2 (2012-01-23)¶
- Fix some packaging issues.
- Remove tox.ini.
- Bump Copyright years.
- Update standard template.
- Eliminate the need to use 2to3, and fix some Python 3 deprecations.
1.1.1 (2012-01-01)¶
- Ensure all built-in schemes are registered by importing them in the __init__.py file.
1.1 (2011-12-31)¶
- Add user-friendly password generation API.
1.0 (2011-12-31)¶
- Initial release.