CLICK HERE TO READ THE INDONESIAN VERSION
Store Passwords Securely in Database using SHA256 — ASP .NET Core
Store passwords in the plain-text, encrypted, hash function, and combination of hash, salt, and pepper
Recently we have heard many cases of cyber-attacks and data theft in Indonesia. Therefore, storing passwords securely is one of the things that we must implement in today’s application development. This article will discuss ways to store passwords in the database, from the worst to the most secure.
Plain-Text
Storing passwords in the plain-text is the worst thing we can do. If someone has access to the database, that person can immediately find out the password of each registered user.
Unfortunately, there are still many programmers who do this. I’ve come across applications that store passwords in plain-text form many times, both private and government-made applications.
It turns out that the application that I made ten years ago saves passwords in plain-text form, with the combo “admin admin” as username and password as well 😆
Never store passwords in the plain-text form!
Encrypted
Storing encrypted passwords in the database is better than storing passwords in plain-text form.
There are two types of encryption used: reversible encryption and non-reversible encryption.
If the password is encrypted using reversible encryption, we can retrieve the actual password by decrypting it.
The downside of reversible encryption is that if the attacker can guess the encryption logic, they can easily retrieve the actual passwords.
In the table above, the saved passwords have been encrypted. It is very difficult for ordinary people to know the value of an encrypted password. However, if we pay attention, admin and budi have the same PasswordEncrypted
value. If one of them is leaked, we can assume all users who have the same password are also leaked.
Hash Function
Using a hash function to store passwords in the database is the most common. Hashes are non-reversible encryption; even if someone has access to the database and knows the hash algorithm used, they cannot decrypt the hash of the password. The commonly used hash algorithms are SHA256 and SHA512.
Although theoretically hash can’t be decrypted, hash functions are not entirely secure. There are several techniques to get the original password from a hash: brute-force, dictionary, and rainbow-table.
Similar to the encrypted password, admin and budi have the same hash value. And if we google the hashed password, the result is:
The original password appears in the first Google search results 😅
Therefore, it is very important to apply password constraints to the applications we create. For example, a minimum password of 8 characters containing uppercase letters, lowercase letters, numbers, and symbols.
Best Practice: Hash + Salt + Pepper + Iteration
The best way to store passwords in the database is to add salt, pepper, and iteration. What does it mean?
Salt is a “condiment” added to the hashing process so that the hash value is different even though the original password is the same. Salt is a random value that is unique to each user. Salt can be stored in the database without needing to be encrypted. Adding salt to the hashing process will make it impossible to obtain the hash via search engines or a hash decryptor application. The value of the salt must be changed every time there is a change in user data, for example, a change in password or email.
Pepper is also a “condiment” added to the hashing process. Pepper is common to all users; all users in the application will use the same pepper. Pepper can be stored in application config, environment variables, or key vault.
Iteration is the number of iterations performed in the hashing process. The more iterations, the harder it will be for the attacker to guess.
Using salt and pepper, even if admin and budi have the same password, the password hash generated will be different.
Password Hash Implementation in Web API
Now we will try to implement the use of Hash + Salt + Pepper + Iteration into a Web API.
Create Web API Project
First of all, create an ASP .NET Core Web API project:
Add the EntityFrameworkCore.Sqlite package from the NuGet Package Manager.
Below is the project structure that we will create:
Entities
Add the User
class to the Entities folder:
Data Context
Add the DataContext
class to the Data folder:
Resources
In the Resources folder, add these record classes: LoginResource
, RegisterResource
, and UserResource
:
Password Hasher
In the Services folder, we will create a class for computing a hash with salt, pepper, and iteration. The class name is PasswordHasher
:
PasswordHasher
class has two functions: ComputeHash()
andGenerateSalt()
.
ComputeHash()
is a recursive function to generate a hash. The hash algorithm used is SHA256.
The process of combining password, salt, and pepper happened in line 13:var passwordSaltPepper = $”{password}{salt}{pepper}”;
Similar to cooking, the process of adding salt and pepper can be adjusted according to taste until the desired complexity is reached 😆
The hash result in the byte array is converted to a base 64 string, which is then re-entered as a password parameter into the ComputeHash() function until the iteration process is complete.
GenerateSalt()
is a function to generate a salt from random bytes and convert it as a base 64 string.
Services
In the Services folder, add the IUserService
interface and the UserService
class:
The UserService
class has two methods: Register()
and Login()
.
In the Register()
method, a salt creation process occurs, which is then stored in the PasswordSalt
column in the User
table:
PasswordSalt = PasswordHasher.GenerateSalt()
Next, the password hashing process using salt, pepper, and iteration:
user.PasswordHash = PasswordHasher.ComputeHash(resource.Password, user.PasswordSalt, _pepper, _iteration);
The value of the _pepper
is retrieved from the Environment Variables, and the value of _iteration
is set to 3.
Controllers
In the Controllers folder, add a controller named UserController
:
Program.cs
Register the DataContext
and UserService
in the Program.cs file:
Set the connection string in the appsettings.json file:
Let’s Try
Run the application and do register and login:
I have created two users: admin and budi. Both users have the same password: admin. The password hashes are different even though they have the same password:
The complete source code of this article can be found here:
Thank you for reading 👍