Spring Security comes with multiple PasswordEncoder implementations. I recommend Md5PasswordEncoder at a minimum, or ShaPasswordEncoder with "256" specified as the constructor arg. As well, you need to up the number of iterations, as the value defaults to 1 if unchanged. To configure this in XML, you will want to use something like the following:
<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider"> <property name="passwordEncoder"> <bean class="org.springframework.security.authentication.encoding.ShaPasswordEncoder"> <constructor-arg value="256"/> <property name="iterations" value="1000" /> </bean> </property> <property name="saltSource"> <bean class="org.springframework.security.authentication.dao.ReflectionSaltSource"> <property name="userPropertyToUse" value="id" /> </bean> </property> </bean>(Editor's note: I've removed some of the other configuration we use in our app. Go figure - wanting to be secretive about our security settings... :)
Now why do we need to go to all this trouble? Let's answer that by backing up a bit and asking some simpler questions.
1) Why don't we store passwords in clear text? Obviously, because if someone were to breach our database and see what is stored in the user table, they could take control of any of the accounts. So, we store the data securely. The best way to do that is to hash the password (thereby destroying the original value) and place the hash in the database. Now, if someone breaches the database, it will be less than obvious what the value is. But what if they have a dictionary of the "usual suspects"? Hash algorithms are deterministic, yielding the same value each time they are given the same inputs. So, if a bad guy sends the dictionary through the hash algorithm, what comes out the other side is the hash of that password. This is called a rainbow table, a map from a hash value to the password it represents, and is commonly used to attempt to crack security on companies that think they are doing the right thing. Adding salt to the hash changes the output because it changes the inputs- and there are a whole lot of options when it comes to salt values. This strengthens the algorithm and, as long as your hash is safe, makes it much more difficult to attack the algorithm.
2) Why the do we need salt? If we use the same salt for every user, then once a bad guy breaks one password, he can rebuild the rainbow table and attempt to look for obvious passwords in the rest of the user table. So, we have two choices - create a random salt from each user, or derive the salt based on the user information. The former strategy has the downfall of forcing us to store the user's salt so we can use it during login. Storing the salt means the same user table we are trying to protect can be its own downfall - if the bad guy gets the table, he has the salt necessary to generate as many rainbow tables has he needs. The latter strategy, deriving the salt, means we use the information we have about the user to figure out a good salt value. Above, I pointed the algorithm to the ID of the User Details object, but you could use something else on the record. The only requirement is that the property being used be immutable, because if it changes, you lose the ability to match the original password, which was hashed with the original salt value.
3) Last thing (I promise): Why the 1000 iterations? During a brute force attack, the bad guy just starts trying passwords in the attempt of guessing the correct value. By forcing 1000 iterations of the hashing algorithm, it makes the algorithm take 1000 times longer to determine if the password matches or fails. For normal users, this is more or less inconsequential, as the end result of the algorithm is typically a successful login. For a bad guy, though, the vast majority of the attempts will be failures, so the "1000 times longer" really adds up. This is called key stretching, and increases the cost of brute force attacks three orders of magnitude.
Well, I hope this helps you get strong security in place for your budding application. All I ask is for you to thank me when you don't have your passwords stolen during an attack.
A question, you say that is not good idea to use a random salt because you would need to store it in the user table which is the one you want to protect, but using the user information in this case the ID doesn't have the same problem? it is stored in the user table as well.ReplyDelete
Good point! We actually came to the same conclusion. I came up with an algorithm that uses salt from the DB combined with salt from another source. This makes it hard for an attacker to brute force the hashed passwords with a rainbow table because the attacker needs both parts. Even better, if the attacker doesn't know about both parts, the salt in the DB will act as a red herring!Delete
(I should put up a blog entry for that.)