Documentation
¶
Overview ¶
Package passgo is an extensible password hashing library with native support for the features of modern password hashing algorithms.
Quick Start ¶
Hashing a password:
hash, err := passgo.Hash(password)
if err != nil {
// handle error
return
}
// store hash in database
Comparing a password against a hash:
status, err := passgo.Compare(hash, password)
if err != nil {
if errors.Is(err, passgo.ErrWrongPassword) {
// handle incorrect password
return
}
// handle unexpected error
return
}
// log the user in
When logging a user in, you can optionally take additional measures for security:
if status.ValidationError != nil {
// prompt the user to update their password
return
}
if status.OutdatedHash {
// re-hash the existing password
}
This approach will use a configuration based on recommended defaults, including a default hashing algorithm.
Customization ¶
You can customize password validation and hashing behavior by creating a Hasher. The API remains the same.
import "codefloe.com/binanary/passgo/algorithms"
hasher := passgo.NewHasher(algorithms.Argon2id{}, passgo.HasherOptions{MinChars: 15})
hash, err := hasher.Hash(password) // Returns an error if password is under 15 characters
status, err := hasher.Compare(hash, password)
The HasherOptions struct definition contains more information on algorithm-agnostic customization options.
In general, the parameters passed to NewHasher only impact the creation of new hashes. They will not prevent a correct password from being successfully compared with an existing hash.
For example, this is completely valid:
bcryptHasher := passgo.NewHasher(algorithms.Bcrypt{}, passgo.HasherOptions{})
password := "16characterslong"
bcryptHash, _ := bcryptHasher.Hash(password)
scryptHasher := passgo.NewHasher(algorithms.Scrypt{}, passgo.HasherOptions{MinChars: 17})
_, err := scryptHasher.Compare(bcryptHash, password)
if err == nil {
// this code will run
}
The only exception is HasherOptions.Pepper. Adding or changing this in a system with existing hashes will invalidate all passwords.
The behavior described above makes it easy to switch to a new hashing algorithm. Simply change the algorithm passed to NewHasher and future hashes will use the new algorithm. Existing hashes will continue to work normally.
Consider infrastructure constraints before switching to a new algorithm. For example, a database configured to store the fixed-length outputs of the bcrypt algorithm may truncate or reject the longer, variable-length hashes produced by argon2id.
Outdated Hash Detection ¶
Modern best practices for credential storage involve increasing the security of hashing algorithms over time as hardware becomes more powerful. This includes recreating existing hashes using the more secure configuration the next time each user logs in.
This library assumes that the current algorithm is more secure than any others used previously. Therefore, hashes created using a different algorithm than the hasher performing the comparison are always considered outdated.
bcryptHash := "$2a$10$0J/kHBT7ucbO0O6I9dJgXu9fMIYUOmn0QV5.SKDSD0hO4tYQC4ryS"
argon2idHasher := passgo.NewHasher(algorithms.Argon2id{}, passgo.HasherOptions{})
status, err := argon2idHasher.Compare(bcryptHash, "password")
if err == nil && status.OutdatedHash {
// this code will run
}
Hashes created using the same hashing algorithm are considered outdated if they use less secure work factors.
oldBcryptHasher := passgo.NewHasher(algorithms.Bcrypt{Cost: 10}, passgo.HasherOptions{})
insecureHash, _ := oldBcryptHasher.Hash(password)
newBcryptHasher := passgo.NewHasher(algorithms.Bcrypt{Cost: 12}, passgo.HasherOptions{})
status, err := newBcryptHasher.Compare(insecureHash, password)
if err == nil && status.OutdatedHash {
// this code will run
}
An increase to any work factor is assumed to improve security. Increasing some work factors and changing others such that overall security is not increased will result in false positives for outdated hashes. This behavior can be altered using custom algorithms.
Secure by Default ¶
Throughout this library, unset/zero values will often be replaced with defaults recommended by authoritative sources, including:
- Internet Engineering Task Force (IETF) RFCs
- Internet Research Task Force (IRTF) RFCs
- National Institute of Standards and Technology (NIST) Special Publications
- Open Web Application Security Project (OWASP) Cheat Sheets
- RFCs produced by very large open source projects
All defaults are documented with a rationale or link to the recommendation in the source code. Expect defaults to change over time, including in minor and patch releases.
This library does not constrain manually set values. It will let you use insecure configurations if you don't know what you are doing. Read the documentation for each algorithm you use and every parameter you set.
When in doubt, OWASP's recommendations are good minimums.
Other Features ¶
This summary covers core functionality. There are many other features not described here, including:
- Password salting (enabled by default where supported)
- Optional password peppering (HasherOptions.Pepper)
- Bring your own blocklist (HasherOptions.Blocklist)
- Customizable validation logic (HasherOptions.Validate)
- Extensible design with support for custom algorithms
- Built-in support for the modern PRECIS framework
- Built for goroutines (data races are treated as bugs)
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrBlockedPassword = errors.New("passgo: attempted to hash a blocked password")
ErrBlockedPassword is returned when attempting to hash a password on the blocklist
var ErrInvalidHash error = errors.New("passgo: could not extract id from hash")
ErrInvalidHash is returned when a hashed password does not follow the expected format
var ErrWrongPassword error = errors.New("passgo: password and hash do not match")
ErrWrongPassword is returned when a password was not used to create the hash it is being compared against
Functions ¶
Types ¶
type ErrPasswordTooLong ¶
ErrPasswordTooLong is returned when attempting to hash a password that violates HasherOptions.MaxChars or HasherOptions.MaxBytes
func (ErrPasswordTooLong) Error ¶
func (e ErrPasswordTooLong) Error() string
type ErrPasswordTooShort ¶
ErrPasswordTooShort is returned when attempting to hash a password that violates HasherOptions.MinChars or HasherOptions.MinBytes
func (ErrPasswordTooShort) Error ¶
func (e ErrPasswordTooShort) Error() string
type ErrUnsupportedAlgorithm ¶
type ErrUnsupportedAlgorithm string
ErrUnsupportedAlgorithm is returned when a hashed password uses an unknown algorithms.HashID
func (ErrUnsupportedAlgorithm) Error ¶
func (e ErrUnsupportedAlgorithm) Error() string
type Hasher ¶
type Hasher struct {
// contains filtered or unexported fields
}
Hasher provides custom hashing behavior via Hasher.Hash and Hasher.Compare
If you don't need a specific configuration, use the package-level Hash and Compare functions for recommended defaults.
func NewHasher ¶
func NewHasher(a algorithms.Algorithm, o HasherOptions) *Hasher
NewHasher returns a Hasher that uses the configuration specified by the parameters. Pass a nil algorithm to use the recommended default.
func (*Hasher) Compare ¶
Compare checks a plaintext password against a hash and determines the status of the hash. This method works on properly-formatted hashes created using any registered algorithms.Algorithm.
type HasherOptions ¶
type HasherOptions struct {
// SaltLen sets the length (in bytes) of the salt applied to passwords.
// This will be ignored for algorithms that perform their own salting or do not support salting.
//
// A value below zero will disable salt generation.
SaltLen int
// Pepper is appended to all passwords before hashing and comparing.
//
// WARNING: Existing hashes will fail to match if this is changed.
Pepper []byte
// MinChars sets the minimum number of characters (runes) required in a password.
// The Hash method will return ErrPasswordTooShort if this is violated.
MinChars int
// MinBytes sets the minimum number of bytes permitted in a password.
// The Hash method will return ErrPasswordTooShort if this is violated.
//
// MinChars is generally more intuitive for users.
MinBytes int
// MaxChars sets the maximum number of characters (runes) permitted in a password.
// The Hash method will return ErrPasswordTooLong if this is violated.
//
// Avoid setting this if possible, as it can harm security.
MaxChars int
// MaxBytes sets the maximum number of bytes permitted in a password.
// The Hash method will return ErrPasswordTooLong if this is violated.
//
// Avoid setting this if possible, as it can harm security.
MaxBytes int
// A case-sensitive list disallowed passwords.
// The Hash method will return ErrBlockedPassword if a password appears on this list.
//
// Unlike other values that impact security, this field has no meaningful default.
Blocklist []string
// Custom validation logic to run when hashing a password.
// For otherwise acceptable passwords, the Hash method will return the same error (if any) as this function.
Validate func(password string) (err error)
}
HasherOptions holds algorithm-agnostic hashing options. Leave fields unset to use recommended defaults.
type Status ¶
type Status struct {
// OutdatedHash indicates whether the Hasher is configured to produce more secure hashes than the one provided.
//
// Consider creating a new hash for the password if this is true (unless the user is already being asked to update their password).
OutdatedHash bool
// ValidationError is the error the Hash method would return for the same password given the Hasher's current configuration.
//
// Consider prompting the user to update their password if this is not nil.
ValidationError error
}
Status holds information about a hashed password
Directories
¶
| Path | Synopsis |
|---|---|
|
Package algorithms defines an interface for password hashing algorithms and provides implementations of several popular algorithms using this interface.
|
Package algorithms defines an interface for password hashing algorithms and provides implementations of several popular algorithms using this interface. |
|
internal
|
|