Как я могу получить соль из LDAP?
Организация, в которой я работаю, использует PPolicy (модуль OpenLDAP), чтобы автоматически получать и хэшировать пароли. К сожалению, у меня нет доступа к машине, на которой работает сервер OpenLDAP, поэтому я не могу посмотреть файл конфигурации. Однако из того, что я видел, почти все, кажется, настроено с использованием настроек по умолчанию.
Я хотел бы иметь возможность получить соль для конкретного пользователя. Если я смотрю на атрибуты пользователя, userPassword - это пароль SSHA. Я не вижу ничего о соли для этого конкретного пользователя. В итоге я посмотрел на схему LDAP и там тоже ничего не вижу о солях.
Если бы вы догадались, где соль хранилась для каждого пользователя, где бы она была? Я понимаю, что это расплывчато и, вероятно, не так много информации, но я не могу найти нигде в документах OpenLDAP, которые объясняют, где именно хранятся уникальные соли. Возможно, кто-то, кто ранее настраивал сервер OpenLDAP, знал бы, где находится местоположение по умолчанию.
Спасибо.
3 ответа
В случае с SSHA соль обычно добавляется в хэш SHA1, а затем все кодируется в Base64 (я никогда не видел LDAP, который бы не делал SSHA таким образом). Вы должны быть в состоянии сказать это, посмотрев на атрибут userPassword. Если он длиной 28 символов с символом = в конце, это всего лишь хеш.
Если значение Base64 имеет длину 32 символа или более, оно содержит как хеш, так и соль. Base64 декодирует значение и удаляет первые 20 байтов, это хеш SHA1. Остальные байты являются солью.
Пример:
Base64 encoded hash with salt
userPassword: {SSHA}MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0
Base64 decoded value
SHA1 Hash Salt
--------------------++++
123456789012345678901234
Изменить: После двойной проверки, кажется, что соли переменной длины иногда поддерживаются. Исправлено описание кодировки, чтобы учесть это.
Пост Сиона мне очень помог, спасибо за это! Я думал, что рабочий тест будет хорошим дополнением для кого-то, кто борется с этой темой;).
public class SshaPasswordVerifyTest {
private final static int SIZE_SHA1_HASH = 20;
@Test
public void itShouldVerifyPassword() throws Exception{
String password = "YouNeverGuess!";
String encodedPasswordWithSSHA = "{SSHA}M6HeeJAbwUCzuLwXbq00Fc3n3XcxFI8KjQkqeg==";
Assert.assertEquals(encodedPasswordWithSSHA, getSshaDigestFor(password, getSalt(encodedPasswordWithSSHA)));
}
// The salt is the remaining part after the SHA1_hash
private byte[] getSalt(String encodedPasswordWithSSHA){
byte[] data = Base64.getMimeDecoder().decode(encodedPasswordWithSSHA.substring(6));
return Arrays.copyOfRange(data, SIZE_SHA1_HASH, data.length);
}
private String getSshaDigestFor(String password, byte[] salt) throws Exception{
// create a SHA1 digest of the password + salt
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(password.getBytes(Charset.forName("UTF-8")));
crypt.update(salt);
byte[] hash = crypt.digest();
// concatenate the hash with the salt
byte[] hashPlusSalt = new byte[hash.length + salt.length];
System.arraycopy(hash, 0, hashPlusSalt, 0, hash.length);
System.arraycopy(salt, 0, hashPlusSalt, hash.length, salt.length);
// prepend the SSHA tag + base64 encode the result
return "{SSHA}" + Base64.getEncoder().encodeToString(hashPlusSalt);
}
}
В PHP это сравнивает простой текстовый пароль (обычно вводимый пользователем) с данным хешем ssha (обычно хранится в вашей базе данных):
private function checkSshaPassword($encrypted_password, $password)
{
// get hash and salt from encrypted_password
$base_64_hash_with_salt = substr($encrypted_password, 6);
$hash_with_salt = base64_decode($base_64_hash_with_salt);
$hash = substr($hash_with_salt, 0, 20);
$salt = substr($hash_with_salt, 20);
// hash given password
$hash_given = sha1($password . $salt, true);
return ($hash == $hash_given);
}