Ensuring strong password security is paramount for any organization that values the privacy and security of its users' accounts. Storing passwords in plain text is a grave mistake that can lead to unauthorized access and compromise sensitive data. To mitigate this risk, the National Institute of Standards and Technology (NIST) recommends the use of password salting and hashing. In this article, we will explore the significance of password salting and hashing, understand their individual concepts, and demonstrate their implementation using Python.
- The Problem with Storing Passwords in Plain Text
- Understanding Password Salting
- Exploring Password Hashing
- Introducing Pepper for Added Security
- Implementing Salt and Hash in Python
Storing passwords in plain text is comparable to leaving confidential documents in an unlocked file cabinet - it's an open invitation for potential security breaches. If an attacker gains access to the password database, they can easily retrieve users' passwords and exploit them for malicious purposes.
To address this vulnerability, NIST recommends the adoption of password salting and hashing techniques.
Password salting involves adding a random value (a salt) to each password before hashing it. The purpose of salting is to ensure that even if users have the same passwords, their resulting password hashes will be different.
Salts are typically stored alongside password hashes in the database, and they significantly enhance password security by thwarting various types of attacks. (It's worth mentioning that some vendors recommend storing the salts on a separate location from the passwords.)
By adding salts to passwords before hashing, several types of attacks become significantly more challenging for malicious actors:
- Dictionary attacks: Attackers try common words from their dictionaries to crack passwords. Salting renders dictionary attacks impractical because the same password will have different hash values due to the unique salt.
- Database lookup attacks: Attackers attempt to exploit vulnerabilities in the website database by sending SQL query statements to retrieve valuable information. Salting makes it harder to determine the original password from the hash stored in the database.
- Brute force attacks: Attackers systematically try different password-username combinations until they find the correct one. Salting forces attackers to perform separate brute force attacks for each user instead of applying a single attack across the entire user base.
- Credential stuffing attacks: Attackers leverage previously leaked or breached passwords to gain unauthorized access. Salting invalidates these stolen passwords by generating unique hash values for the same password.
- Rainbow table attacks: Attackers use precomputed tables of passwords and their corresponding hashes to quickly retrieve password values. Salting ensures that even if an attacker possesses such a table, it becomes ineffective due to the uniqueness introduced by the salt.
The second part of password security involves password hashing.
Hashing transforms a plaintext password into a fixed-length string of characters, known as a hash value. One-way hashing functions make it computationally infeasible to reverse-engineer the original password from its hash. After hashing, the password hash is stored in the database.
Popular hashing algorithms such as SHA-3, SHA-256, SHA-512 can, in theory, be used in password hashing. The main issue lies in the fact that the computations needed to calculate the hash are performed quickly, which is good for purposes of verifying the integrity of file hashes but not as good for passwords. This because an attacker can perform these a larger number of calculations on different texts in order to figure out the original text.
For this reasons, people use what is sometimes referred to as "slow hash functions". Some commonly used hashing algorithms include:
- PBKDF2 (Password-Based Key Derivation Function 2)
Let's delve into the concept of peppering, which further enhances the security of password hashing.
In addition to salting, peppering is another technique that enhances password hashing. Peppering involves adding a secret value (pepper) to the password hash, similar to salt. However, unlike salt, peppers are not stored alongside the hashed passwords. They are kept separate, usually in a secure location such as a hardware security module (HSM). By isolating the pepper, even if attackers manage to exploit vulnerabilities in the database, they won't have access to the peppered passwords.
Pepper provides an additional layer of security, and its length should be at least 32 bytes, as recommended by the Internet Engineering Task Force (IETF) for the PBKDF2 key derivation function.
Python offers various libraries and functions to implement password salting and hashing. Let's explore two popular options: PBKDF2 and Argon2.
hashlib module in Python provides the necessary functions to implement PBKDF2. Here's an example code snippet:
import hashlib import os # Generate a salt salt = os.urandom(16) # Derive a password hash using PBKDF2 password = "mysecretpassword".encode("utf-8") iterations = 100000 key_length = 64 hash_name = 'sha256' password_hash = hashlib.pbkdf2_hmac( hash_name=hash_name, password=password, salt=salt, iterations=iterations, dklen=key_length ) # The resulting passowrd hash would look like this #b'u\xe1b\xda\x03\xba\xce\x08\x95\xa2\x8a\x8f\xac\x99\x89\xce@\x86\xac\x02$C\xa7\xd8ey\x97m\xe0\xdb\x7f\xe8"_\x83D>\xc4\x8a\x12~~\xf5oBu\xe3V\xbf\xc8\x0cH;y\xf6g\xdb]\x81\xbc):\x8c\xac' # Store the salt and password hash in the database save_to_database(username, salt, password_hash)
In the above example, we generate a random salt using
os.urandom(). Then, we use the
hashlib.pbkdf2_hmac() function to derive the password hash. It takes parameters like the digest algorithm, password, salt, number of iterations, and key length. Finally, we store the salt and password hash in the database.
Argon2 is a modern and memory-hard password hashing algorithm known for its resistance against various attacks. To use Argon2 in Python, we can leverage the
argon2 library. Here's an example:
import argon2 # Hash a password using Argon2id password = "mysecretpassword" password_hash = argon2.PasswordHasher(salt_len=32).hash(password) # The password_hash looks like this # $argon2<T>[$v=<num>]$m=<num>,t=<num>,p=<num>$<bin>$<bin> || The last two binary chunks (encoded in Base64) are, in that order, the salt and the output # '$argon2id$v=19$m=65536,t=3,p=4$hIrweJvfjghuIKIBIS9t0gP/5UsY51GLL1WgY3Qh6q4$f809Un0cDINVTYOusgJWuNZOPgh8ICByGhxTCKga7BA' # Verify a password is_valid = argon2.PasswordHasher(salt_len=32).verify(password_hash, password)
In this example, we generate a salt and use the
argon2.PasswordHasher().hash() method to compute the password hash using Argon2id. We can later use
argon2.PasswordHasher().verify() to validate a password against its corresponding hash.
Password salting and hashing are essential techniques for enhancing account security and protecting sensitive user data. By incorporating unique salts and robust hashing algorithms like PBKDF2 or Argon2, organizations can significantly reduce the risk of unauthorized access and password-related attacks.
Remember the key takeaways:
- Storing passwords in plain text is a grave security risk.
- Salting passwords adds uniqueness and thwarts various types of attacks.
- Hashing transforms passwords into irreversible hash values.
- Consider peppering for added security, keeping it separate from salts.
- Python provides libraries like
argon2for implementing salt and hash.
- Choose appropriate hashing algorithms based on security requirements.