title: Announcing password-2.0 summary: A library for working with passwords in Haskell tags: haskell draft: false
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.
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
Password
andPasswordHash
types that can be shared throughout your code. These are meant to be simple types with reasonable typeclass instances. You should be able to takepassword
as a dependency and rely on these types from other libraries. -
Easy-to-use functions for hashing a
Password
, and checking aPasswordHash
against aPassword
. -
An optional password-instances package that provides additional typeclass instances for
Password
andPasswordHash
, 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.
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.0
library)
password-2.0
also adds a bunch of much-needed documentation, as well as many
tests.
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:
> :set -XOverloadedStrings
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:
> import Data.Password.Bcrypt
Next, we need a
Password
to work with. Let's create one with
mkPassword
2:
> let pass = mkPassword "foobar"
> pass :: Password
**PASSWORD**
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
:
> let result = checkPassword pass hash
> result :: PasswordCheck
PasswordCheckSuccess
There are a few issues in the password
library that we'd like help with from
anyone interested.
-
Split off the
Data.Password
module into a separate package calledpassword-types
.All of the hashing algorithms provided in
password
come fromcryptonite
.However, we would ideally like people to be able to use the
Password
andPasswordHash
data 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-types
package that just exports everything from the currentData.Password
module (which doesn't require a dependency oncryptonite
). -
Split the
password-instances
package into separate packages.Currently,
password-instances
has instances forPassword
andPasswordHash
for 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
persistent
to access a database, you shouldn't have to transitively depend onpersistent
just to get instances fromaeson
. -
Add more helpful instances to
password-instances
.password-instances
has 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.
password
has 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
checkPassword
andhashPassword
functions to be able to read/write password hashes in other formats. -
Add functionality for validating password complexity requirements.
password
currently 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.
password
mostly 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.
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
FromJSON
instance forPassword
provided bypassword-instances
makes this easy. ↩ -
Note that
PasswordHash
,hashPassword
, andcheckPassword
all have a phantom parameter. This is used to store the algorithm that was used to hash the password. You are not able to callcheckPassword
on a hash produced from a different algorithm'shashPassword
. ↩ -
This is made easier with the
PersistField
instance forPasswordHash
frompassword-instances
. ↩