With work calmed down a bit, I have returned back to refreshing some programming skills. At first, I was reading on some Python but then had an idea that I wanted to start designing so went back to C#. Although I will be using C#, this post will primarily focus on the design implementation because I found it interesting. Each time I implement the login system, I always seem to learn a new little tidbit.
Most of the practices that I exercise does not completely prevent serious hackers. The basic practices will prevent most common intruders and at least make it more difficult for serious hackers. There is little that can be really done against brute force if someone gains access to the password files. Since access to password files is more a network security, that won't be covered in this blog.
By implementing the following, basic users like developers and db admins should not be able to just read the password. This would also means non-technical user who "accidentally" gains access to the files would also have difficulty reading the password. In other words, I should not be able to read the password just by logging into the database because I need to troubleshoot other issues even though I have access to the files. In actuality, I wouldn't be able to reverse the password without going to extreme lengths even if I was the developer or db admin.
Encryption / Hashing Password
Because the password needs to be stored some place, the password must be at least be made unreadable. Today, this can be done very easily with most programming frameworks. Basically all that needs to be done is to use hash algorithm.
Hash algorithm is asymmetric in cryptography meaning that it is extremely difficult to reverse the hash. In most cases, this is sufficient for security implementation of a system. In some cases, you may want to be able to reverse the hash. In those cases, you want to use normal encryption algorithms.
One example that you may choose to use an encryption algorithm is when you need to implement a solution to automatically connect to another system that requires a password. In other words, the password needs to be stored then used to pass to another system. These could be used to access APIs, Web Services, screen-scraping, etc.
Although neither are perfect, anyone hacking into the system would require a certain level of knowledge thus eliminating even basic developers and db admins. There are other layers that can be added to prevent external hackers which I won't be expanding here.
Salt
In conjunction to hashes, salt should also be used with the password. This is an extra "random" data appended to the password. The random data is still something that is based on certain information that needs to be stored. That data could also be encrypted.
In some cases, the user id could also be used as the salt. Although not random, it does make it more difficult for dictionary hackers by creating a different hashed password. This primarily protects users that have the same passwords.
I am not sure what the big advantage is to using a purely random salt. Even if the salt is known, this does not make the hashed password any easier to decrypt. Brute force is more likely to be used which means that the hacker already has access. This means the hacker most likely has access to the salt values. It does add a level of complexity to the hacker, but unlikely orders of degree in difficulty.
To me the most important is to at least differentiate similar passwords. By doing this, a hacker could not just search the table for another user with the same password which can be easily done by a developer or a db admin. All that needs to be done is query the password database with the same hashed password.
Disable Bit
In the table, I like to also include a disable bit. In the off-chance that a password or all passwords have been compromised, all it would take is a simple update to force users to update their passwords. This of course requires that the code makes use of this bit. If it doesn't, this is quite a moot point.
If this is a for a corporate industry level, there should be policies behind enabling passwords if that is an option. It is important to make sure the identity of the person is indeed in satisfaction to the company. For my site of lowest importance, I would force a password change. If I only enable the password, the hacker could just be trying to test to see if it was the same password. By changing the password (assuming that the requester is the hacker), the original user will not be able to sign-in thus triggering the user to either change the password again or inquire why it doesn't work. Hopefully by this point, the user should have received an email of the first password change. If not, likely the email has been compromised. Although I wrote that a bit convoluted, I am basically saying that it is more troublesome to the hacker when the password has a forced reset because he does not get anything out of it.
Audit Log
On top of the above data structure, I like to keep an audit log of any activity especially sign-ins. One great use is to find dictionary hackers or sql injection hackers. With an audit log, you can find particular patterns to how data is entered.
If there have been several failed attempts by the same or similar users, there is a good chance that it is a dictionary hacker. Once identified, certain actions can take place. You can automatically blacklist the ip or user. If user, you may want to warn the user that someone has failed to attempt to sign-in a certain number of times (in the off-chance that it may actually be the user who forgot their password). I think a 100 attempts within one sitting (~5-10 minutes) is a range to set an alert. Most people won't try more than 50 times (include frustration entries) before they quit or request for a password change.
If sql injection hacker, you can easily identify that someone is trying to compromise your system. Just remember to check for sql injection before the encryption. There should not be a need to do an exception rejection of password if using encrypted/hashed password because the sql injection would also be encrypted/hashed which would make it unreadable to the sql engine. This check is primarily to identify potential threats.
Side idea: I think it would be a neat idea to have a client-side password encrypter or hasher. Basically whenever you enter into a password field, whatever you type in will be encrypted/hashed before sending to the server side. This would at least allow users to appear to have multiple passwords (by using different keys like site name into the algorithm). This would prevent multiple accounts from being compromised if a single system is compromised.
MS SQL Insert
I did not find it intuitive in how to insert a binary, so I figure this might help someone (ie my future self... why is he so forgetful?).
string s = "INSERT INTO LGN (UserPassword) VALUES (@binaryValue)";
string c = "data source=.\\XX;Initial Catalog=XXXX;Integrated Security=SSPI;";
SqlCommand cmd = new SqlCommand(s, new SqlConnection(c));
cmd.Parameters.Add("@binaryValue", SqlDbType.VarBinary, 500).Value = text;
cmd.Connection.Open();
cmd.ExecuteNonQuery();
cmd.Dispose();
Of course, use your normal try/catch/finally clauses.
Cons
There is a disadvantage to hashing the password in that you cannot edit the password once it is set without having the user reset their password or login again. A scenario where you want to edit the password is if you need to add salt or change your key.
There is a collision in hashing the password which is around 0.001%. This means that there is a very rare scenario where two passwords end up with the same hash password. Although a possibility, the amount of attempts should trigger other security issues that can still address this con.
Reference:
http://money.cnn.com/2012/06/06/technology/linkedin-password-hack/index.htm
http://en.wikipedia.org/wiki/Salt_(cryptography)