Skip to main content
Security

Hashing, Salting and Peppering

7 mins

lock and key surrounded by salt and pepper shakers, symbolizing the techniques of hashing (lock and key) combined with salting and peppering to enhance security.

Although the terms hashing, salting, and peppering may sound like cooking techniques, they are actually security measures used to protect sensitive data, such as passwords, from unauthorized access. These techniques are commonly used in web applications to secure user credentials and other sensitive information.

Rather than explaining the concepts in detail, we will use questions that may arise in when storing sensitive data such as passwords in a database. The answers to these questions will help you understand the importance of hashing, salting, and peppering.

How should passwords be stored in a database? #

Your database is a treasure trove of sensitive information, including user passwords. There may be security access controls in place to may prevent unauthorized access to the database, but what if the database is compromised? What if there are rogue employees with access to the database?

Storing passwords in plain text is a bad idea. If an attacker gains access to the database, they can easily read the passwords and use them to access user accounts or sell them to cyber-criminals.

Hashing passwords is a better approach. Hashing is a one-way process that converts plain text into a fixed-length string of characters. The hash function is deterministic, meaning that the same input will always produce the same output. When a user creates an account or changes their password, the password is hashed and stored in the database. When the user logs in, the password they enter is hashed and compared to the stored hash. If the hashes match, the user is authenticated.

Common hash functions include SHA-256, SHA-512, and bcrypt. These functions are designed to be computationally expensive, making it difficult for attackers to reverse-engineer the original password from the hash.

When the account is created:

  1. User creates an account or changes their password.
  2. Password is hashed using a secure hash function like SHA-256.
  3. Hashed password is stored in the database.
graph TD A[Password123] --> B[Hash] B --> C[5f4dcc3b5aa765d61d8327deb882cf99]

When the user logs in:

  1. User enters their password.
  2. Password is hashed using the same hash function.
  3. Hash is compared to the stored hash.
  4. If the hashes match, the user is authenticated.
graph TD A[Password123] --> B[Hash] B --> C[Calculated Hash 5f4dcc3b5aa765d61d8327deb882cf99] C --> D[Database] D--> |Retrieve| E[Stored Hash 5f4dcc3b5aa765d61d8327deb882cf99] E -->|Match| G[Authenticated]

So far so good, the passwords are hashed and cannot be reversed to their original form. So anyone who gains access to the database will not be able to read the passwords.

What is the problem with hashing passwords? #

Humans are creatures of habit. They tend to use the same passwords across multiple accounts. Since the hash of a password is always the same, an attacker can use a technique called rainbow table attack to crack the passwords.

Rainbow Table Attack #

A rainbow table is a list of precomputed hashes for a large number of possible passwords. By comparing the hashes in the rainbow table to the hashes in the database, an attacker can quickly find the original passwords.

This is why websites insist on using strong passwords with a mix of uppercase and lowercase letters, numbers, and special characters. The more complex the password, it is effectively less common, and less likely to be in a rainbow table.

The rainbow attack relies on the common passwords. If the password is unique, it is not likely to be in the rainbow table.

Salting #

Salting turns a common password into a unique one. A salt is a random string of characters that is added to the password before hashing. The salt is stored alongside the hash in the database. When the user logs in, the salt is added to the password before hashing, and the resulting hash is compared to the stored hash.

So consider a password “password123”. Without salting the hash would be

hash("password123") = 5f4dcc3b5aa765d61d8327deb882cf99

With salting, the hash would be

salt = "s3cr3t";
hash("password123" + "s3cr3t") = 7c6a180b36896a0a8c02787eeafb0e4c

The rainbow table may have the hash for “password123”, but it is unlikely to have the hash for “password123” + “s3cr3t”. That is, there is no lookup key for the hash ‘7c6a180b36896a0a8c02787eeafb0e4c’ in the rainbow table.

When the account is created:

  1. User creates an account or changes their password.
  2. A random salt is generated.
  3. Salt is added to the password.
  4. Salted password is hashed using a secure hash function like SHA-256.
  5. Salt and hashed password are stored in the database.
graph TD A[Password123] --> B[Salt] B --> C[Password123Salt] C --> D[Hash] D --> E[7c6a180b36896a0a8c02787eeafb0e4c]

When the user logs in:

  1. User enters their password.
  2. Salt is retrieved from the database.
  3. Salt is added to the password.
  4. Password is hashed using the same hash function.
  5. Hash is compared to the stored hash.
graph TD A[Password123] --> B[Salt] B --> C[Password123Salt] C --> D[Hash] D --> E[Calculated Hash 7c6a180b36896a0a8c02787eeafb0e4c] E --> F[Database] F--> |Retrieve| G[Stored Hash 7c6a180b36896a0a8c02787eeafb0e4c] G -->|Match| H[Authenticated]

What is the problem with salting passwords? #

Salting is a great way to protect passwords, but it is not foolproof. If an attacker gains access to the database, they gain access to the salts as well. They can use a technique called dictionary attack to crack the passwords.

Dictionary Attack on Salted Passwords #

A dictionary attack is a method used by attackers to crack passwords by systematically trying every word in a predefined list of common passwords (the “dictionary”). Here’s how it works in the context of salted password storage:

  1. Obtain Hashes and Salts: The attacker first needs access to the database containing the salted password hashes and the corresponding salts.

  2. Create a List of Common Passwords: The attacker uses a dictionary list, which is a list of common passwords that many users might choose (e.g., “password123”, “qwerty”, “123456”).

  3. Hash Each Password with the Salt: For each password in the dictionary list:

    • The attacker retrieves the salt corresponding to the hash they are trying to crack from the database.
    • They concatenate the salt with the password from the dictionary.
    • They then hash the combined string (salt + password) using the same hash function that was used to create the original hashed passwords.
  4. Compare Hashes: The attacker compares the resulting hash with the hashes stored in the database.

    • If a match is found, the attacker has successfully cracked the password because the hashed result of the salt and password from the dictionary matches the stored hash.

Peppering #

Peppering is another technique used to enhance the security of salted passwords. Peppering involves adding a secret key to the password before hashing. The secret key is not stored in the database, making it difficult for attackers to crack the passwords.

When the account is created:

  1. User creates an account or changes their password.
  2. A random salt is generated.
  3. A secret key is added to the password.
  4. Salt and secret key are added to the password.
  5. Salted and peppered password is hashed using a secure hash function like SHA-256.
  6. Salt and hashed password are stored in the database.
  7. Secret key is stored securely, not in the database.
graph TD A[Password123] --> B[Salt] B --> C[Password123Salt] C --> D[Secret Key] D --> E[Password123SaltSecretKey] E --> F[Hash] F --> G[7c6a180b36896a0a8c02787eeafb0e4c]

When the user logs in:

  1. User enters their password.
  2. Salt is retrieved from the database.
  3. Secret key is retrieved from a secure location.
  4. Salt and secret key are added to the password.
  5. Password is hashed using the same hash function.
  6. Hash is compared to the stored hash.
graph TD A[Password123] --> B[Salt] B --> C[Password123Salt] C --> D[Secret Key] D --> E[Password123SaltSecretKey] E --> F[Hash] F --> G[Calculated Hash 7c6a180b36896a0a8c02787eeafb0e4c] G --> H[Database] H--> |Retrieve| I[Stored Hash 7c6a180b36896a0a8c02787eeafb0e4c] I -->|Match| J[Authenticated]

Peppering is an additional layer of security that makes it even more difficult for attackers to crack passwords. The secret key is not stored in the database, so even if an attacker gains access to the database, they will not be able to crack the passwords without the secret key.