With the release of EPC 141070 that introduces cryptographic hash functions for RDMLX, it’s a good time to talk about password security. Read on if you are developing or maintaining websites that store passwords in a user database.
We’ll talk about some best practices that you can employ to minimise the risk of your users’ passwords falling into the wrong hands. We’ll also talk about how to do cryptographic hashing in RDMLX.
Never Store Passwords in Plain-Text
Never assume that your system is unbreachable. System breaches and database theft happen more often than you think. Design and code your system assuming that your database will be stolen at some point.
If an attacker can find out a user’s password from the stolen database, it’s bad for two reasons:
- The attacker can use the password to login to your site and potentially access unauthorised information.
- And even worse, since end-users are notorious for using the same password for multiple websites (e.g. Gmail and Facebook), your stolen database provides the means for the hacker to attack another website.
Do not Use Reversible Encryption to Encrypt the Passwords
Even though it’s better than storing passwords in clear, it’s not recommended to use reversible encryption as it requires an encryption key, which introduces an unnecessary vulnerability (an attacker just needs to find the key in order to decrypt the passwords).
Hash Your Passwords using Cryptographic Hash Functions
Cryptographic hash functions takes an input and produces a fixed-size value ( the “hash”) from that input. All hash functions have a common characteristic: it’s one way – it’s computationally very hard to calculate the original input value from the hash value. This is in contrast with encryption where we can decrypt an encrypted value back to its original form.
Hashing a password and storing the hash value in a database table is the most secure approach, since nobody, including your web application, can calculate the original password. If your user database is stolen, it would be very hard for the attacker to obtain the actual passwords.
Calculating Hash Value Using XPRIM_Crypto_Hash
You can calculate hash value of a string using XPRIM_Crypto_Hash and XPRIM_Binary.
Hash functions operate on bytes instead of a string, so we’d need to first convert the password string into bytes using XPRIM_Binary.
* Assuming the password we want to hash is in #PASSWORD field * And we want to place the resulting hash value in the #HASH field Define_Com Class(#XPRIM_Crypto_Hash) Name(#HashFunction) Define_Com Class(#XPRIM_Binary) Name(#PasswordBytes) Define_Com Class(#XPRIM_Binary) Name(#HashBytes) * Convert the password string (#PASSWORD) to bytes (#PasswordBytes) #PasswordBytes.FromStringUsingUTF8 String(#PASSWORD) * Calculate the hash value of #PasswordBytes, output to #HashBytes #HashFunction.Compute Input(#PasswordBytes) Result(#HashBytes) * Convert #HashBytes to hex string for storage in the database #HASH := #HashBytes.AsHexString
Which Hash Algorithms Should We Use for Password Hashing?
There are quite a few hashing algorithms out there, but not all of them are suitable for password hashing. The RDMLX Extended Library currently supports a couple of hashing algorithms, but the ones suitable for password hashing are PBKDF2 and SCrypt. Normal hash functions such as MD5 and SHA shouldn’t be used for password hashing as they are fast to calculate, which means it’s vulnerable to brute-force attack.
The example below shows how to tell our XPRIM_Crypto_Hash component to use PBKDF2, with 1000 iteration (the higher the iteration, the slower it will take to calculate the hash value, the more secure the system will be). But remember that we must balance security with usability. Your users will abandon your website if it takes 5 seconds to login.
#HashFunction.UsePBKDF2 Iteration(1000) #HashFunction.Compute Input(#PasswordBytes) Result(#HashBytes)
Is it enough to hash the password?
Well, not quite. It’s easy for hackers to pre-compute the hash of all common dictionary words (and common known passwords) to create a lookup table that they can use to do a “reverse” lookup of a hash value to find the original input value. With the hash values in the stolen database, they can very quickly find the original password using the lookup table.
So how do we thwart the lookup-table attack?
it’s actually quite simple. The lookup-table approach will stop working if the same password always produces different hash values every time it’s getting hashed (but of course we should still be able to verify that the password matches the hash value).
The solution is to append a sequence of random bytes (the “salt”) to the password before we hash it. We don’t need to keep the salt value secret. Generally we would just store the salt value alongside the password in the same database table. The most important thing is that salt value should never get re-used. Every time a password is hashed, a new salt value should be generated. Every user in the database should use different salt value.
Ideally, the salt value should generated using a cryptographically secure random number generator. However, the current version of the Library doesn’t have that facility yet (it will be in the next release), so for the time being we will use the *GUID to illustrate the concept.
#SALT := *GUID #PASSWORD := #PASSWORD + #SALT * Convert the password string (#PASSWORD) to bytes (#PasswordBytes) #PasswordBytes.FromStringUsingUTF8 String(#PASSWORD) * Calculate the hash value of #PasswordBytes, output to #HashBytes #HashFunction.Compute Input(#PasswordBytes) Result(#HashBytes) * Convert #HashBytes to hex string for storage in the database #HASH := #HashBytes.AsHexString * Keep both #SALT and #HASH in the user database table