Never store passwords in plain text or just their hash: it seems a no-brainier, but there are still plenty of people doing that. I present the why and how on this subject, offering my own real life solution that you can implement right away, bringing your solution beyond military grade security.
The most common mistake is storing passwords in clear text, accompanied by the equally dangerous mistake of sending them in clear text over the network, the latter based on the naïve assumption that an SSL
connection grants enough security.
SSL connectivity alone cannot guarantee security: every week new vulnerabilities are identified and fixed, and that shows how wrong it is to assume that SSL alone grants security. That assumption also implies that all users always install the latest security fixes, and that is another quite wrong assumption.
Now let's talk about "salting" mistakes, but first let's define it: when we add something to a password to make it more complex, we say that we are adding salt to it. For instance, if the user chooses mypass
as his/her password, we might want to generate a random number to salt
it, let's say 1234
. We add that to the original password, so getting the salted
Salting the password and then storing the salt in the database is another common mistake: why? Because doing that we assume that the attacker is an entity outside of the company, but that is a incorrect assumption. What about a disgruntled employee dumping the client’s database on the Torrent network
? This is becoming an issue we encounter frequently, trending all over the world because of current bad employment practices. We need to remember that threats do not come only from outside the company, they might as well hit us from within, and those are the most dangerous.
Because of this we should never store any salt in the database. We make sure that we code the algorithm that computes the salt in a deterministic way. We also need to ensure that the algorithm is known only by a close circle of people, and that its parts have been developed either within the "circle of trust"; if this is not possible then make sure to break it down into methods that can be aggregated separately and have those produced by different people. The full source code should never be available outside the company's "circle of trust".
One other definition we now need to continue is "hashing". The actual dictionary definition brings us quite close to what we do: we break down into pieces and scramble the bits of a word or phrase, and in doing so we reduce it to a fixed length string of hexadecimal
values. To do so we implement algorithms that have been approved by NIST
(and other organizations). In this algorithm we'll use MD5
and SHA256 and SHA512
The password generation process in details
The above implies that the first password hashing must always happen on the client side, and the result of that is sent over to the server. But is this safe enough? Not entirely, we need to add “salt“ to ensure security.
What does that mean? Let’s look into it from an attacker's perspective. The common way to hack an account is to use lookup tables to find common passwords, so the hacker takes the encrypted password that he found on the hacked database and runs it against a database of password hashes like the followings:
The above are simple test resources, in reality any hacker has a seriously complete database compiled against full phraseological dictionaries and thesauruses in multiple languages: that’s what we are up against. Have you tested your email on Have I Been Pwned?
That resource (quite nice!) can tell you if your email was part of a known security breach, and you can see what the breach was about. Most likely your email address and your hashed password have been compromised, and if the hash was simply the MD5 or SHA256 of the password itself then a hacker relying on a good dictionary database will most likely get your password back in clear text.
Because of this we have to be very careful, starting with establishing a strong password protection algorithm. Mine is shown in the following diagram.
Let’s see this in a step by step practical example from a real world algorithm in use in DFT Games Ltd games (very simplified version). In the sign up phase, the user types his user name, let’s assume it’s the email address as it’s a common scenario (we force it lower case to ensure determinism in the next steps):
then he types his password:
Such weak passwords are painfully common, so we have to make sure we correct this to protect the client and zero out our liabilities
. To do that, let’s compute our salt from the user name. Here I use one of many possible approaches, any other is ok as long as it’s deterministic.
Because the first letter is “j” its value is 152 in ASCII, an even number, therefore we pick all odd characters from the user name, so that
becomes for our purpose the following string:
Now we hash this new string, and for this step a simple MD5 will be enough, giving us the following:
Now we have all we need to fix that weak password, so we chain all together adding a star character in between just to increase the complexity, getting the following string:
my password* d9e2feaea42f0f4b6891f8030f357041
Now this looks much better and it is quite hard to hack using common tools, therefore this is what we are now going to hash using SHA512, getting our first secure value as all lower case Base 64
Well, how can we improve this even more? For instance, we can apply the same odd/even rule to this hash, making sure that, because “j” is even, every non numeric character in an odd position is upper case, getting this final string:
This final one is not just the SHA512 of a salted password, but has been parsed to apply a letter casing rule derived by the user name exponentially reducing the already dramatically low odds to be able to crack the code via any brute force attack, with or without any hash database.
But... will this be enough? Not really! We still could be cracked given enough computing power because the only source of this hash are the user name and password: we need to add something on top of this on the server side. A good way to do this is the one I show on the diagram above. Basically when the user signs up, the server creates the user record first, then it takes the record time-stamp as a string, and strips all the white spaces from it:
then selects a part of this new string, maybe using an approach similar to the one used for the password by the client algorithm, but with some changes, like odd/even value of the third character in the password hash sent by the client, getting a result like this:
Then we concatenate this new information to the hash received from the client to salt it, and we hash that again via SHA512, getting the final hash
If you want, you can also perform the upper case process on this result, to make it harder to crack. The result of all the above steps is the one we store in the database. We'll perform the very same steps then when the user signs in again in the future.
There is no dictionary attack that can crack this algorithm, no matters how good the hacker is... even the US NSA and CIA combined would certainly fail to crack this, no matter how many resources could be put on the task!
Naturally the above steps are just a sample, a suggestion: be creative and original
, don't just use it as it is in this article because... I just published it, so it's now possible to crack it :) Use different combinations to compute your salt on both client and server and make sure it's complex enough that it cannot be easy derived.
The actual full algorithm of the above real life implementation is in use in our family company, DFT Games Ltd., and the real details about the salt is known only to family members and provided via a scrambled DLL to the development teams for in-app implementation. Keeping the client and server algorithms a secret is key to make this really secure. On top of this we have two-factor authentication based on our own authenticator app (and algorithm) for specific high security applications.
© Copyright Giuseppe "Pino" De Francesco - 2016