DEV Community

Cover image for Salty Hashes
Kent Riggs
Kent Riggs

Posted on

Salty Hashes

Secure Password Storage: Hashing and Salting

It's time for another plain language blog post. This time about a serious topic. Salty hashes. Mmm... breakfast, right? Not so much. Hashing and salting is standard practice in modern software development where user authentication is required to ensure sensitive passwords are not stored in plaintext in case of a security breach.

When building applications that require user authentication, securely storing passwords is a critical concern. Hashing is the process of passing a password through a one-way cryptographic function to generate a fixed-length string known as a hash. Even minor changes to the input password result in a vastly different hash output.

Hashing: Transforming Passwords

Common hashing algorithms used for password storage include SHA-256, SHA-3, Argon2 and bcrypt. Here's a simple example of how hashing works with the SHA-256 algorithm as a first line of defense:

Password: "myPassword123"
SHA-256 Hash: 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92
Enter fullscreen mode Exit fullscreen mode

The key security benefit of hashing is its one-way nature. While inputting a password will always generate the same hash, the hash is just a sequence of hexadecimal characters that bears no resemblance to the original password. This makes it virtually impossible to derive the password from the hash alone.

Salting: Unique Keys for Every Password

While hashing provides a solid first line of defense, it's still vulnerable to certain types of attacks, such as rainbow tables (pre-computed tables of hashes for common passwords). This is where salting comes into play.

Salting is the process of adding a random string of data, known as a salt, to the password before hashing it. This means that even if two users have the same password, their hashed values will be different due to the unique salt used for each password.

Password: "myPassword123"
Salt: "Xt7&Hy9z"
Salted Password: "Xt7&Hy9zmyPassword123"
SHA-256 Hash: a8b24e9718c1fc9824f93c5d2e98988e9974f31a1d148634308a9a726f5e3d54
Enter fullscreen mode Exit fullscreen mode

The salt is typically stored alongside the hashed password in the database. During authentication, the salt is retrieved, combined with the entered password, and the resulting string is hashed. If the newly generated hash matches the stored hash, the user is authenticated.

Secure Authentication Flow

  1. - User enters password during login
  2. - App retrieves stored salt and hash for that user
  3. - User password is combined with stored salt
  4. - The salted value is hashed using the same algorithm
  5. - If newly generated hash matches stored hash, user is authenticated

Best Practices

  • Use strong, modern hashing algorithms: Algorithms like bcrypt and Argon2 are designed specifically for securely hashing passwords and are preferred over general-purpose hashes like SHA-256.

  • Use a unique salt for each password: Never reuse the same salt across multiple passwords, as this would diminish the security benefits of salting.

  • Increase work factor over time: Many hashing algorithms allow you to configure a "work factor" that determines how computationally expensive the hashing process is. Increase this factor over time to keep up with advancements in hardware capabilities, ensuring that password hashing remains sufficiently slow to mitigate brute force attacks while not overly taxing legitimate authentication attempts.

  • Don't roll your own implementation: Use well-tested, industry-standard libraries and frameworks for hashing and salting. Implementing cryptographic functions from scratch is risky business.

Hashing and Salting in Practice

Most modern web frameworks and libraries provide built-in tools for securely hashing and salting passwords. One example is in Python's Flask framework using the bcrypt module:

 @hybrid_property
    def password_hash(self):
        return self._password_hash

    @password_hash.setter
    def password_hash(self, password):

        password_hash = bcrypt.generate_password_hash(
            password.encode('utf-8'))
        self._password_hash = password_hash.decode('utf-8')

    def authenticate(self, password):
        return bcrypt.check_password_hash(
            self._password_hash, password.encode('utf-8'))
Enter fullscreen mode Exit fullscreen mode

The example above handles salting and hashing (using a strong algorithm like bcrypt), verifies the entered password against the stored hash and salt which then returns True if the password matches the stored hash, otherwise returns False

This gives an overview of the how and why for salting and hashing when you're building an app that requires user authentication. One last reminder, be sure to stay up to date with dependencies in your app to ensure it stays safe from exploitation as technology evolves.

The following resources go into detail on types of hashing algorithms and their differences and more technical detail on the process itself

Now... I am off to find myself a greasy spoon kind of diner to get a salted hash breakfast for dinner.

Top comments (0)