Как хранить и использовать соль Широ из базы данных

Я использую Широ в приложении для аутентификации. Я использую хешированный пароль с солью и храню их в своей базе данных следующим образом:

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

    ByteSource salt  = randomNumberGenerator.nextBytes(32);

    byte[] byteTabSalt  = salt.getBytes();

    String strSalt = byteArrayToHexString(byteTabSalt);

    String hashedPasswordBase64 = new Sha256Hash(inPassword, salt, 1024).toBase64();

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

Я храню соль со строкой в ​​моей базе данных. Теперь в моем мире я хочу вернуть свои данные из базы данных, для этого я использую сервис транзакций. Но моя соль - Strong, поэтому я хочу, чтобы она снова превратилась в тип ByteSource со статическим методом:

ByteSource byteSourceSalt = Util.bytes(salt); //where the salt is a String

Но когда я создаю свой SaltedAuthenticationInfo, он не авторизуется.

Я думаю, что моя проблема в моем методе конвертации:

private String byteArrayToHexString(byte[] bArray){

        StringBuffer buffer = new StringBuffer();

        for(byte b : bArray) {
            buffer.append(Integer.toHexString(b));
            buffer.append(" ");
        }

 return buffer.toString().toUpperCase();    
}

Спасибо за вашу помощь.

4 ответа

Решение

Как упоминалось в превосходном ответе /questions/771493/kak-hranit-i-ispolzovat-sol-shiro-iz-bazyi-dannyih/771501#771501, служба DefaultPasswordService Shiro уже генерирует уникальные соли для каждого пароля.

Однако нет необходимости реализовывать собственный PasswordService для добавления частной соли (иногда называемой "перцем") к солям для каждого пользователя. Частная соль может быть настроена в 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-код для генерации подходящего хэша пароля:

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");

Полученный хеш выглядит так:

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

Частная соль не хранится в базе данных, что затрудняет взлом паролей, если злоумышленник получает доступ к дампу базы данных.

Этот пример был создан с использованием shiro-1.2.2

Спасибо https://github.com/Multifarious/shiro-jdbi-realm/blob/master/src/test/resources/shiro.ini за помощь с синтаксисом для shiro.ini

Вы смотрели на PasswordMatcher / PasswordService?

Это уже имеет всю встроенную логику кодирования / декодирования / сравнения. Чтобы использовать это:

Хранение пароля в базе данных:

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);
}

Тогда вы можете просто использовать PasswordMatcher в качестве сопоставителя в вашей сфере.

realm.setCredentialsMatcher(new PasswordMatcher());

или в shiro.ini:

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

Реализация DefaultPasswordService автоматически добавляет случайную соль в каждый вызов encryptPassword. Эта "публичная" соль будет храниться в "hashedPasswordBase64", который вы получаете от "encryptPassword".

Поскольку "открытая" соль генерируется индивидуально для каждого хешированного пароля, нельзя "просто" создать радужную таблицу и перебрать все ваши хешированные пароли сразу. Для каждого хешированного пароля злоумышленник должен создать собственную уникальную радужную таблицу из-за уникальной "публичной" соли. Пока вам не нужно вносить дополнительную соль в базу данных.

Чтобы сделать сохраненные хешированные пароли еще более безопасными, вы также можете добавить "приватную" соль, которая должна храниться где-либо еще - до тех пор, пока ее нет в базе данных. Используя "приватную" соль, вы можете защитить хешированные пароли от атаки "грубой силы" по радужным таблицам, поскольку злоумышленник не знает "частную" соль и не может получить "приватную" соль из записей базы данных.

Это очень простой пример того, как создать PasswordService, который использует "приватную" соль, представленную как константную строку, и которая работает как CredentialsMatcher:

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

Затем вы можете использовать свою собственную реализацию в shiro.ini:

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

Этот пример был создан с использованием shiro-1.2.2

Я меняю свой тип для сохранения моей соли. Теперь я использую byte[] вместо String.

ByteSource salt  = randomNumberGenerator.nextBytes(32);

byte[] byteTabSalt  = salt.getBytes();

И я храню byteTabSalt в своей базе данных.

Другие вопросы по тегам