How to stock and use a shiro's salt from database

家住魔仙堡 提交于 2019-12-03 03:11:38
Mikael Falkvidd

As mentioned in the excellent answer https://stackoverflow.com/a/20206115/603901, Shiro's DefaultPasswordService already generates unique salts for each password.

However, there is no need to implement a custom PasswordService to add a private salt (sometimes called "pepper") to the per-user salts. Private salt can be configured in shiro.ini:

[main]
hashService = org.apache.shiro.crypto.hash.DefaultHashService
hashService.hashIterations = 500000
hashService.hashAlgorithmName = SHA-256
hashService.generatePublicSalt = true
# privateSalt needs to be base64-encoded in shiro.ini but not in the Java code
hashService.privateSalt = myVERYSECRETBase64EncodedSalt
passwordMatcher = org.apache.shiro.authc.credential.PasswordMatcher

passwordService = org.apache.shiro.authc.credential.DefaultPasswordService
passwordService.hashService = $hashService
passwordMatcher.passwordService = $passwordService

Java code for generating a matching password hash:

DefaultHashService hashService = new DefaultHashService();
hashService.setHashIterations(HASH_ITERATIONS); // 500000
hashService.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME);
hashService.setPrivateSalt(new SimpleByteSource(PRIVATE_SALT)); // Same salt as in shiro.ini, but NOT base64-encoded.
hashService.setGeneratePublicSalt(true);

DefaultPasswordService passwordService = new DefaultPasswordService();
passwordService.setHashService(hashService);
String encryptedPassword = passwordService.encryptPassword("PasswordForThisUser");

The resulting hash looks like this:

$shiro1$SHA-256$500000$An4HRyqMJlZ58utACtyGDQ==$nKbIY9Nd9vC89G4SjdnDfka49mZiesjWgDsO/4Ly4Qs=

The private salt is not stored in the database, which makes it harder to crack the passwords if an adversary gains access to a database dump.

This example was created using shiro-1.2.2

Thanks to https://github.com/Multifarious/shiro-jdbi-realm/blob/master/src/test/resources/shiro.ini for help with the syntax for shiro.ini

Have you looked at PasswordMatcher / PasswordService?

This already has all of the encoding/decoding/compare logic built-in. To use it:

Storing password in database:

PasswordService service = new DefaultPasswordService(); // or use injection or shiro.ini to populate this

private User createUserWithHashedPassword(String inName, String inFirstName, String inLastName, String inPassword){

  String hashedPasswordBase64 = service.encryptPassword(inPassword);

  return new User(inName,inFirstName,inLastName,hashedPasswordBase64,strSalt);
}

Then you can simply use PasswordMatcher as the matcher in your realm.

realm.setCredentialsMatcher(new PasswordMatcher());

or in shiro.ini:

matcher = org.apache.shiro.authc.credential.PasswordMatcher
realm.credentialsMatcher = $matcher

The DefaultPasswordService implementation automatically adds a random salt to each encryptPassword call. That "public" salt will be stored within the "hashedPasswordBase64" that you receive from "encryptPassword".

Because the "public" salt is individually generated for each hashed password one cannot "simply" generate a rainbow table and brute-force all your hashed passwords at once. For each hashed password the attacker would have to generate an own, unique rainbow table because of the unique "public" salt. So far you do not need to put an extra salt into the database.

To make your stored hashed passwords even more secure you can furthermore add a "private" salt that should be stored anywhere else - as long as not in the database. By using a "private" salt you could protect the hashed passwords against a brute-force rainbow-table attack, because the attacker does not know the "private" salt and cannot gain the "private" salt from the database entries.

This is a very basic example how to create a PasswordService that utilizes a "private" salt provided as a constant string and that works as CredentialsMatcher:

public class MyPrivateSaltingPasswortService extends DefaultPasswordService
{
   public MyPrivateSaltingPasswortService()
   {
      super();
      HashService service = getHashService();
      if (service instanceof DefaultHashService)
      {
         ((DefaultHashService) service).setPrivateSalt(
             new SimpleByteSource("MySuperSecretPrivateSalt"));
      }
   }
}

you then could use your own implementation in shiro.ini:

[main]
saltedService = com.mycompany.MyPrivateSaltingPasswortService
matcher = org.apache.shiro.authc.credential.PasswordMatcher
matcher.passwordService = $saltedService
realm.credentialsMatcher = $matcher

This example was created using shiro-1.2.2

I change my type for the save of my salt. Now I'm using a byte[] instead of a String.

ByteSource salt  = randomNumberGenerator.nextBytes(32);

byte[] byteTabSalt  = salt.getBytes();

And I stock the byteTabSalt in my database.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!