General security

Best Practices When Creating Passwords

Adrian Stolarski
September 17, 2012 by
Adrian Stolarski

I understand perfectly well that the InfoSec website has often discussed the use of good algorithms for hashing passwords. But these articles were usually very vague. So in this article you can familiarize yourself with creating good passwords in your applications.

It is an article which was completely by accident. Recently they asked me what I thought about coding data in databases, so I had to think carefully about this problem. In total, I have created an algorithm that allows us to perform some operations in high security, and I wrote an article about hashing and salting passwords.

FREE role-guided training plans

FREE role-guided training plans

Get 12 cybersecurity training plans — one for each of the most common roles requested by employers.

FREE role-guided training plans

FREE role-guided training plans

Get 12 cybersecurity training plans — one for each of the most common roles requested by employers.

Many people think that simply hashing passwords provides a sufficient level of safety - for example, abandoning the MD5 to SHA-1 / SHA-2. Such reasoning has one small drawback, namely, there is no need to "break" the hash to get the user password. Sounds ridiculous?

Having shot a table with users is sufficient to perform a simple query, so that everything becomes clear:

[plain]SELECT *

FROM users

WHERE password = SHA1 (login);[/plain]

From the execution of this question we will receive a list of users whose password is the same as login. The method seems to be of little sense, because who registers for the same password as the login name? On the other hand, how many sites do you find that allow this?

Theory

The problem of identity theft with login and password is so serious that to gain access to someone's account it is not needed to access the database. Just create a simple crawler, which retrieves a list of users available on the site, and then try to login stating found logins, as data logging.Generating user passwords should always be approached with great responsibility. Builders of applications are generally pleased if a user enters a password and gets access privileges only to certain parts of the application. After the user enters the password, they are not interested in what he does next, and applications assume that if the user entered a password, then it is credible.

It is our duty as developers is to protect the end users of our application before the attack, by providing mechanisms for passwords, which should not fall into the wrong hands. This is a more important thing than protecting web applications from attack. Today we learn to generate passwords by mixing different values.

When users provide passwords, the application only needs to know that what the user provides is correct. This can be done with a simple string comparison. Assuming we keep the user's application data in the database, look at where there is a corresponding match of a username and password. If they match, it allows the user access to top secret stuff, and we go about our day. The content of the password string is optional, so ...

Salinity is Very Easy

There are several methods for mixing sequences, such as those md5 () or sha1 (). They have their pluses and minuses, but this is not covered here. The important thing is that we choose the method that works only in one direction, so that mcrypt and base64_encode rather fall off. As a dev from Java, for the purpose of our article, I chose the algorithm of md5 () and write each implementation, which is a great example of using md5 () and the function of salt:

[plain]java.security.GeneralSecurityException import;

java.security.MessageDigest import;

public class Hashing

{

public static StringBuffer hexOne (String password) throws GeneralSecurityException {

MessageDigest md = MessageDigest.getInstance ("MD5");

md.update (password.getBytes ());

byteData byte [] = md.digest ();

/ / Convert the byte to hex format for method 1

StringBuffer sb = new StringBuffer ();

for (int i = 0; i <byteData.length; i + +) {

sb.append (Integer.toString ((byteData [i] & 0xff) + 0x100, 16). substring (1));

}

return sb;

}

public static StringBuffer hexTwo (String password) throws GeneralSecurityException {

MessageDigest md = MessageDigest.getInstance ("MD5");

md.update (password.getBytes ());

byteData byte [] = md.digest ();

/ / Convert the byte to hex format for method 2

HexString StringBuffer = new StringBuffer ();

for (int i = 0; i <byteData.length; i + +) {

Integer.toHexString String hex = (0xff & byteData [i]);

if (hex.length () == 1) hexString.append ('0 ');

hexString.append (hex);

}

hexString return;

}

public static void main (String [] args) throws Exception

{

String password = "123456";

System.out.println ("Digest (in hex format) ::" + hexOne (password). ToString ());

/ / Convert the byte to hex format for method 2

System.out.println ("Digest (in hex format) ::" + hexTwo (password). ToString ());

}

}[/plain]


Well, we have a pretty good code, but still it does not have a decent degree of entropy. This code is not yet ready for any serious applications. The problem we meet is that people very often use passwords that are similar to each other, or passwords based on the dictionary. Every attacker knows that very well, so the first thing you will use, will attempt to attack either by force or without input.

But we must still be one step ahead of evil hackers, so we have to raise the bar and use the salt to protect the password. Salt is useful to everything that is encrypted, to make it more difficult to break. In fact, there are countless ways to salt the passwords, but to choose the appropriate method, we must remember one thing: we need a well-secured salt storage location for our passwords. It is very important because if a hacker manages to steal the salt to the password then both password and salt are useless.

Basically, in this article we will be using two methods. In the first one we will create a salt for all passwords, and in the second, we will generate the salt for each password separately.

Remember that even using one salt for each password is acceptable when creating the application, although it can occur after minor problems, such as two or more identical terms. We will not change all the code in our class, and only the main method:

[plain]public static void main (String [] args) throws Exception

{

/ / Orginal password

String password = "123456";

/ / Add salt

String salt = "mysecretpassword";

/ / Convert the byte to hex format for method 1

System.out.println ("Digest (in hex format) ::" + hexOne (password). ToString ());

/ / Convert the byte to hex format for method 2

System.out.println ("Digest (in hex format) ::" + hexTwo (password). ToString ());

/ / Convert the byte to hex format method 1 + salted

System.out.println ("Digest (in hex format) ::" + hexOne (password). ToString () + "." + HexOne (salt). ToString ());

/ / Convert the byte to hex format for method 2 + salted

System.out.println ("Digest (in hex format) ::" + hexTwo (password). ToString () + "." + HexTwo (salt). ToString ());

}[/plain]


Now we fix up our base algorithm. We must remember that there is nothing to prevent our use of hash algorithms up to two or three, so let's add a method of treating any string of characters to encoded characters. For our base class, add the two methods:

[plain]private static String convertToHex (byte [] data) {

StringBuffer buf = new StringBuffer ();

for (int i = 0; i <data.length; i + +) {

halfbyte int = (data [i] >>> 4) & 0x0F;

two_halfs int = 0;

do {

if ((0 <= halfbyte) && (halfbyte <= 9))

buf.append ((char) ('0 '+ halfbyte));

else

buf.append ((char) ('a' + (halfbyte - 10)));

halfbyte = data [i] & 0x0F;

} While (two_halfs + + <1);

}

buf.toString return ();

}

public static String SHA1 (String text) throws NoSuchAlgorithmException, UnsupportedEncodingException {

MessageDigest md;

md = MessageDigest.getInstance ("SHA-1");

byte [] sha1hash = new byte [40];

md.update (text.getBytes ("iso-8859-1"), 0, text.length ());

md.digest sha1hash = ();

return convertToHex (sha1hash);

}[/plain]


Well done. Now we can use a single algorithm to encode the password, and the second to the encoding of salt. But we have a little difficulty. Add the two salts. One is first encoded text, and the other at the end. See the revised code of main methods:

[plain]public static void main (String [] args) throws Exception

{

/ / Orginal password

String password = "123456";

/ / Add salt

String salt = "mysecretpassword";

Salt_two String = "xcvg # $% ^% WEEW # $ $% ^% $ ^% ^ HFGH";

/ / Convert the byte to hex format for method 1

System.out.println ("Digest (in hex format) ::" + hexOne (password). ToString ());

/ / Convert the byte to hex format for method 2

System.out.println ("Digest (in hex format) ::" + hexTwo (password). ToString ());

/ / Convert the byte to hex format method 1 + salted

System.out.println ("Digest (in hex format) ::" + hexOne (password). ToString () + "." + HexOne (salt). ToString ());

/ / Convert the byte to hex format for method 2 + salted

System.out.println ("Digest (in hex format) ::" + hexTwo (password). ToString () + "." + HexTwo (salt). ToString ());

/ / Convert the byte to hex format method 1 + two salted

System.out.println ("Digest (in hex format) ::" + hexOne (password). ToString () + sha1 (salt_two). ToString () + "." + "." + HexOne (salt). ToString ( ));

/ / Convert the byte to hex format for method 2 + two salted

System.out.println ("Digest (in hex format) ::" + sha1 (salt_two). ToString () + "." + HexTwo (password). ToString () + "." + HexTwo (salt). ToString ( ));

}

While we're at the stage of fairly serious coding, we can create a beautiful way to encode passwords, you will use not only for hashing passwords, but then to later write them into the database, or to verify the password entered by the user. Here is the code of our new features:

public static String unification_one (String password, String salt, String salt_two) throws GeneralSecurityException, UnsupportedEncodingException, GeneralSecurityException {

Hashed_one String = hexOne (password). ToString () + sha1 (salt_two). ToString () + hexOne (salt). ToString ();

hashed_one return;

}

public static String unification_two (String password, String salt, String salt_two) throws GeneralSecurityException, UnsupportedEncodingException, GeneralSecurityException {

Hashed_two String = sha1 (salt_two). ToString () + hexTwo (password). ToString () + hexTwo (salt). ToString ();

hashed_two return;

}[/plain]


Now to add the main method at the end of two lines:

[plain]/ / Convert the byte to hex format method 1 + two salted second method

System.out.println ("Digest (in hex format) ::" + unification_one (password, salt, salt_two). ToString ());

/ / Convert the byte to hex format for method 2 + two salted second method

System.out.println ("Digest (in hex format) ::" + unification_two (password, salt, salt_two). ToString ());[/plain]


The Birthday Issue

This topic is perfectly described in the link http://mste.illinois.edu/reese/birthday/intro.html. First, let's look at the problem in terms of mathematics. This issue is part of mathematical statistics and probability. But we are interested in one thing: If it is likely that despite the use of salt, there will be two of the same password. The likelihood is there and it is really big. Now the question arises, how to avoid it?

Just have any of our users generate a separate password. This prevents the so-called birthday issue in which the commonly used passwords and single salts make duplicates of the password in our database. But remember that the use of unique passwords compels us to complete user control.

How then do we generate a random string? It allowed me to prepare a feature that will do it for us. The parameter will take the string length and type, how it will generate the sample strings. See, that is an ideal feature for generating passwords. Here's the code:

[plain]salted public String (int length, int choice) {

@ SuppressWarnings ("resource")

Scanner in = new Scanner (System.in);

String password = null;

length = in.nextInt ();

choice = in.nextInt ();

Random rand = new Random ();

randNum int = 0;

for (int passLength = 1; passLength <= length; passLength + +)

{

if (choice == 1)

{

randNum = rand.nextInt (26) + 97;

}

else if (choice == 2)

{

randNum = rand.nextInt (123);

while (randNum <65 | | randNum> 90 && randNum <97)

randNum = rand.nextInt (123);

}

else if (choice == 3)

{

randNum = rand.nextInt (123);

while (randNum <65 | | randNum> 90 && randNum <97 && randNum <48 | | randNum> 57)

randNum = rand.nextInt (123);

}

else

{

randNum = rand.nextInt (123);

while (randNum> 65 && randNum <90 | | randNum> 97 | | randNum> 48 && randNum <57 | | randNum> 33 && randNum <47)

randNum = rand.nextInt (123);

}

password + = (char) randNum;

}

password.toString return ();

}[/plain]


Now let's see how we developed the algorithm. I promise you that we will use everything we did before. Our algorithm will work like this:

  1. Download password
  2. Payment of the first salt
  3. Cash on the other salt
  4. Generate three random strings
  5. Coding of both salt and password by sha1, md5, sha1
  6. Joining three random strings
  7. Coded entirely by md5
  8. Joining two strings of random characters at the beginning and end of the chain created
  9. Coded entirely by sha1

It seems complicated right? But it is really very simple. And if you have all the features and they work, it will be your homework. The use of this class is great, as you can see.

Summary

We have another layer of security, but there is also a way to overcome it. The need for only "some" time and computing power. You have the opportunity to remember that hash functions are designed so that they are fast at work in favor of the attacker. An attacker can not act even more effectively. Since the hash function is deterministic, an attacker can perform the calculation again, and get results on a number of occasions. Add to that a little salt. Its purpose is to force the attacker to have to repeat the calculation for each user individually. If there is no salt, the attacker has the word password hash, and then checks to see if any of the hashes in the database password is set e10adc3949ba59abbe56e057f20f883e.

If the salt is used, each record must be tested to calculate the hash for the password and the correct value of salt. He can not use the tables, because he would have to prepare a separate version of the tables for each possible value of salt, which would take both the "little" time and space. We still have a unique salt for each user, but to get it, you need to access or to guess the value of the secret.

Salt does not defend us against the user who chooses simple dictionary passwords. If a database password leaks, there is a chance that the password will be broken. The attacker may have enough time and desire to see the typical value of passwords for each user individually. To make life even more difficult for an attacker, we can choose a way of hashing passwords which is computationally heavy. Is it possible to somehow deal with users who use simple passwords?

FREE role-guided training plans

FREE role-guided training plans

Get 12 cybersecurity training plans — one for each of the most common roles requested by employers.

FREE role-guided training plans

FREE role-guided training plans

Get 12 cybersecurity training plans — one for each of the most common roles requested by employers.

In this context, in case of leakage their passwords remain secure, even though they are weak. A design by adding to the system one more secret comes to mind. Before hashing passwords based on this additional secret and overt, and unique user-salt, generated a new salt, which is only used when hashing the password. An attacker would have to correctly guess two secrets, the user's password and mastersalt, and one secret it is easier to protect.

Adrian Stolarski
Adrian Stolarski

Adrian Stolarski is a freelance security tech blogger, specializing in Java, PHP, and JQuery. In his own words, he does the hard work of training the unemployed. Currently, he handles Evaluation Visualization for real-time systems with XWT and Eclipse RAP. If he sees that something works, he asks how it works and why it works, then sets out to make it work better. A researcher for InfoSec Institute, he currently lives in Poland, but plans to move to London.