2020-05-18
I recently helped with the release of the password-2.0 library.
This 2.0 release corresponds to a big rewrite of the password library, spearheaded entirely by Felix Paulusma.
The biggest improvement in 2.0 is the addition of a few new options for hashing algorithms, including Argon2, Bcrypt, and PBKDF2.
What is the password library?
The password library was originally intended to be used by people implementing username/password authentication in web applications, but it is now easily usable by anyone dealing with passwords and password hashing in any type of application.
There are four main advantages of using password:
Canonical
PasswordandPasswordHashtypes that can be shared throughout your code. These are meant to be simple types with reasonable typeclass instances. You should be able to takepasswordas a dependency and rely on these types from other libraries.Easy-to-use functions for hashing a
Password, and checking aPasswordHashagainst aPassword.An optional password-instances package that provides additional typeclass instances for
PasswordandPasswordHash, likeFromJSON(from aeson), FromHttpApiData (from http-api-data), and PersistField (from persistent). These instances are frequently used when doing actual web development.Great documentation. Anything confusing in the Haddocks is considered a "high-priority" bug.
What changed in password-2.0?
password-2.0 is a complete rewrite of the password library by Felix Paulusma1. The architecture of the library has been completely redone to support multiple hashing algorithms.
The following algorithms are now supported:
- Argon2
- Bcrypt
- PBKDF2
- Scrypt (this was the only algorithm supported in the original
password-1.0library)
password-2.0 also adds a bunch of much-needed documentation, as well as many tests.
How to use password?
This section will present a short tutorial for using password. The following commands are run in a GHCi session. You can follow along if you start GHCi with a command like stack --resolver nightly-2020-05-16 ghci --package password-2.0.1.1.
Enable the OverloadedStrings extension to make the following code a little easier:
The first step in using password is to pick which hashing algorithm you want to use. If you don't know which to pick, we recommend Bcrypt. The trade-offs of the different algorithms are discussed in more detail in the Haddocks.
Once you've picked the hashing algorithm, you can import its module. We use Data.Password.Bcrypt as an example:
Next, we need a Password to work with. Let's create one with mkPassword2:
Note that the Show instance for Password doesn't actually show the password. This is a security measure. You don't have to worry about passwords leaking into your web application logs.
Now that you have a password, we can hash it with the hashPassword function3. This produces a PasswordHash:
> hash <- hashPassword pass
> hash :: PasswordHash Bcrypt
PasswordHash {unPasswordHash = "$2b$10$IbnlktUyGjeunSlE4bQtfu89LY6veyDW0CGIoR5Kj8qrQa916txMS"}This PasswordHash can be stored in a database4.
The checkPassword function can be used to check a Password against a PasswordHash:
Future Work
There are a few issues in the password library that we'd like help with from anyone interested.
Split off the
Data.Passwordmodule into a separate package calledpassword-types.All of the hashing algorithms provided in
passwordcome fromcryptonite.However, we would ideally like people to be able to use the
PasswordandPasswordHashdata types as canonical data types in different libraries on Hackage. We don't want all these packages to have to pull in a transitive dependency oncryptonite.Ideally, we would have a
password-typespackage that just exports everything from the currentData.Passwordmodule (which doesn't require a dependency oncryptonite).Split the
password-instancespackage into separate packages.Currently,
password-instanceshas instances forPasswordandPasswordHashfor type classes fromaeson,http-api-data, andpersistent.Ideally, there would be a more fine-grained way to pick only the instances you want.
For instance, if you're not using
persistentto access a database, you shouldn't have to transitively depend onpersistentjust to get instances fromaeson.Add more helpful instances to
password-instances.password-instanceshas helpful instances from a few widely used libraries, but it is missing many more.If you use
password, please consider contributing missing instances!Support different hash formats.
passwordhas its own way of encoding hashes. In the above example, you can see that the Bcrypt algorithm outputs hashes that look like this:$2b$10$IbnlktUyGjeunSlE4bQtfu89LY6veyDW0CGIoR5Kj8qrQa916txMS.This works well if you're writing your application from the beginning with
password, but if you're trying to interoperate with password hashes produced by other languages and frameworks, you'll have to do some manual conversion.It would be nice to extend the
checkPasswordandhashPasswordfunctions to be able to read/write password hashes in other formats.Add functionality for validating password complexity requirements.
passwordcurrently doesn't provide any functionality for validating the complexity of passwords. It would be nice if it provided an API for checking whether passwords are at least a given length, have a certain number of uppercase, lowercase, and special characters, etc.This might be of interest to anyone who likes designing easy-to-use APIs for complicated use-cases.
Perform a security audit.
We'd love to get an in-depth review from any security-minded Haskellers.
passwordmostly just wraps around functionality provided bycryptonite, but it would still be great to get a review for the code we do have.If you find any big issues, feel free to email me directly if you don't feel comfortable creating a public issue on GitHub.
Please feel free to send pull requests or create issues for any of the above enhancements.
Conclusion and Thanks
I need to give a big thanks to Felix Paulusma for password-2.0. He is solely responsible for almost all the work that went into this 2.0 release. Felix is now a full maintainer of password.
There were a few other people who contributed as well:
Malte Brandy and Falko Peters helped with an issue about a timing attack when checking password hashes.
Tristan de Cacqueray helped to fixed up some of our Haddocks.
Thanks to everyone, and I hope we get more contributions in the future!
Footnotes
In reality, you probably wouldn't create a password by hand like this, but instead get one in a JSON request. The
FromJSONinstance forPasswordprovided bypassword-instancesmakes this easy.↩︎Note that
PasswordHash,hashPassword, andcheckPasswordall have a phantom parameter. This is used to store the algorithm that was used to hash the password. You are not able to callcheckPasswordon a hash produced from a different algorithm'shashPassword.↩︎This is made easier with the
PersistFieldinstance forPasswordHashfrompassword-instances.↩︎
tags: haskell