Hashing and salting

In today's digital world, data security is becoming an increasingly pressing issue. With the rise in cyber attacks and information leaks, protecting users' sensitive data is a priority for software developers. One of the key security techniques is hashing and salting passwords and other critical data.

Hashing is the process of converting raw data into a unique, fixed-length encrypted code. This method is widely used to protect passwords, since even if the hashed data is leaked, it is extremely difficult to recover the original password. However, hashing by itself is not always sufficient for complete security. Attackers can use attacks such as rainbow tables to find the original values ​​from their hashes.

To enhance security, salting is used – adding random data to the original passwords before hashing them. This approach makes it much more difficult for attackers because each hash becomes unique, even if multiple users have the same password. As a result, rainbow table attacks and other password guessing techniques become less effective.

In this article, we'll take a closer look at the concepts of hashing and salting and their role in secure software design.

Encryption uses a combination of asymmetric and symmetric techniques. For example, an SSL digital certificate conveys the identity and public key of a website. This provides the user with information to help determine the owner of the website, including who issued the certificate, how long it is valid, what the site's public key is, and more.

Since anyone can create a digital certificate, the certificate itself is useless unless it is verified by a trusted third party. These trusted parties are known as Certificate Authorities (CAs), and by default we all trust a number of CAs that act as the gatekeepers of the Internet.

Digital certificates are a mechanism used to exchange a public key to initiate the encryption process between you and a site.

To protect your privacy online, you need SSL/TLS and HTTPS—or Secure Socket Layer/Transport Layer Security and Hypertext Transfer Protocol Secure. Web traffic is not encrypted by default and is open to interception and recording.

This is acceptable in situations where your data is public or has low sensitivity, but if you have high privacy requirements, such as protecting a user's password, you will need to implement network-level encryption to protect confidentiality.

Another form of privacy protection is the use of hashing. Please note that hashing is not encryption.

This is a one-way hash because computing the hash is very fast, but recovering the original data from the hash value is extremely slow, almost impractical. How can we use this to protect privacy?

Consider this login page. In insecure systems, someone may store the password in a database, which is a bad practice. Instead, hashing can help protect privacy by storing a hash of the password in a database.

When users authenticate, we regenerate the hash since one-way traffic is fast, and then check to see if the hashes match. If they match, we know the user is logged in correctly. In this way, we protect the confidentiality of passwords.

There are several problems with hashing, such as lookup tables. Attackers can take general lists of words or passwords and pre-generate hashes for each of those passwords.

There is a possibility that some users may use the same password, so if the hashes match, you will receive the password and be able to log in with the account.

In the case below, since we always use the same hashing algorithm, we can conclude that these three users have the same password. You may not know what exactly the password is, but you've got some information just by looking at the table. What can we do to avoid this?

To prevent such attacks, you can use a technique known as salting. A salt is a random value that is added to the password before hashing. This makes each hash unique, even if users use the same passwords. This makes lookup tables useless since a separate table will need to be created for each salt.

We can overcome this problem by combining hashing with another important concept known as salt. Adding salt to a hash means adding random data to the message so that it does not always produce the same result. This protects against attacks in which attackers try to guess common passwords to create collisions.

By ensuring that each password is hashed differently in a random manner, no two identical passwords will ever hash to the same value.

Thus, an attacker who has access to all the hashed passwords and their corresponding salts will not be able to use lookup table attacks—unless they create a lookup table with exactly the same salt values ​​for each password. Always use salt for your hashes.

Let's look at an example implementation in python

We will use the hashlib library for hashing and os to generate a random salt.

Salt generation:

salt = os.urandom(16)

We use os.urandom(16) to generate a random salt of 16 bytes (128 bits). This will ensure that the salt for each password is unique.

Password hashing:

hash_object = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)

We use the pbkdf2_hmac function from the hashlib library to hash the password.

Function parameters:
'sha256' is the hashing algorithm.
password.encode() – password converted to bytes.
salt — generated salt.
100000 is the number of hashing iterations (the more, the safer, but slower).

Returning salt and hash:

return salt, hash_object

We return a pair (salt, hash) which can then be stored in the database.

Password verification:

hash_object = hashlib.pbkdf2_hmac('sha256', provided_password.encode(), stored_salt, 100000)

return hash_object == stored_password

To verify a password, we hash the provided password using the stored salt and compare the result with the stored hash. If the hashes match, the password is correct.

Usage example

Hashing and output of salt and hash:

password = 'my_secure_password'

salt, hashed_password = hash_password(password)

print(f"Salt: {salt.hex()}")

print(f"Hashed Password: {hashed_password.hex()}")

We hash the password and output the salt and hash in hexadecimal format.

Checking correct and incorrect password:

is_valid = verify_password(hashed_password, salt, 'my_secure_password')

print(f"Password is valid: {is_valid}")

is_valid_wrong = verify_password(hashed_password, salt, 'wrong_password')

print(f"Password is valid: {is_valid_wrong}")

We check for correct and incorrect passwords and display the results of the verification.

As a result, we got:

import hashlib
import os

def hash_password(password):
    # Генерируем случайную соль
    salt = os.urandom(16)  # 16 байт (128 бит) - стандартный размер соли
    # Конкатенируем пароль и соль и хешируем результат
    hash_object = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)
    # Возвращаем соль и хеш в виде пары (salt, hash)
    return salt, hash_object

def verify_password(stored_password, stored_salt, provided_password):
    # Хешируем предоставленный пароль с той же солью
    hash_object = hashlib.pbkdf2_hmac('sha256', provided_password.encode(), stored_salt, 100000)
    # Сравниваем хеши
    return hash_object == stored_password

# Пример использования:
password = 'my_secure_password'
salt, hashed_password = hash_password(password)

print(f"Salt: {salt.hex()}")
print(f"Hashed Password: {hashed_password.hex()}")

# Проверка пароля
is_valid = verify_password(hashed_password, salt, 'my_secure_password')
print(f"Password is valid: {is_valid}")

is_valid_wrong = verify_password(hashed_password, salt, 'wrong_password')
print(f"Password is valid: {is_valid_wrong}")

You can get more practical skills in building reliable and secure software architecture as part of practical online courses from industry experts.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *