Шрифт:
Напомним, вы должны сами выбрать значение затравки и сохранить его в результате, который возвращает PBKDF2. Хорошую затравку можно получить от функции os.urandom(8), которая вернет восемь случайных байтов криптографического качества, полученных от операционной системы.
Предположим, что вы хотите проверить пароль, имея затравку и валидатор. Сделать это просто:
def validate(typed_password, salt, validator):
if PBKDF2(typed_password, salt) == validator:
return True
else:
return False
Мы использовали стандартный алгоритм SHA1 и счетчик итераций 10000. Функция PBKDF2 легко переводится на любой язык. Вот, скажем, реализация на С с использованием алгоритма SHA1 из библиотеки OpenSSL:
#include <openssl/evp.h>
#include <openssl/hmac.h>
#define HLEN (20) /* используем SHA-1 */
int pbkdf2(unsigned char *pw, unsigned int pwlen, char *salt,
unsigned long long saltlen, unsigned int ic,
unsigned char *dk, unsigned long long dklen) {
unsigned long l, r, i, j;
unsigned char txt[4], hash[HLEN*2], tmp[HLEN], *p = dk;
unsigned char *lhix, *hix, *swap;
short k;
int outlen;
if (dklen > (((unsigned long long)1)<<32)-1)*HLEN) {
abort;
}
l = dklen/HLEN;
r = dklen%HLEN;
for (i=1; i <= l; i++) {
sprintf(txt, "%04u", (unsigned int)i);
HMAC(EVP_sha1, pw, pwlen, txt, 4, hash, &outlen);
lhix = hash;
hix = hash + HLEN;
for (k=0; k < HLEN; k++) {
tmp[k] = hash[k];
}
for (j=1; j < ic; j++) {
HMAC(EVP_sha1,pw, pwlen, lhix, HLEN, hix, &outlen);
for (k=0; k < HLEN; k++) {
tmp[k] ^= hix[k];
}
swap = hix;
hix = lhix;
lhix = swap;
}
for (k=0; k < HLEN; k++) {
*p++ = tmp[k];
}
}
if (r) {
sprintf(txt, "%04u", (unsigned int)i);
HMAC(EVP_sha1, pw, pwlen, txt, 4, hash, &outlen);
lhix = hash;
hix = hash + HLEN;
for (k=0; k < HLEN; k++) {
tmp[k] = hash[k];
}
for (j=1; j < ic; j++) {
HMAC(EVP_sha1,pw, pwlen, lhix, HLEN, hix, &outlen);
for (k=0; k < HLEN; k++) {
tmp[k] ^= hix[k];
}
swap = hix;
hix = lhix;
lhix = swap;
}
for (k=0; k < r; k++) {
*p++ = tmp[k];
}
}
return 0;
}
А вот код, написанный на C#:
static string GetPBKDF2(string pwd, byte[] salt, int iter) {
PasswordDerivedBytes p =
new PasswordDerivedBytes(pwd, salt, "SHA1", iter);
return p.GetBytes(20);
}
Рекомендации по выбору протокола
Если вы аутентифицируете пользователей в уже сложившейся среде, в которой применяется инфраструктура паролей на базе Kerberos, то этой инфраструктурой и следует пользоваться. В особенности это относится к случаю, когда нужно аутентифицировать пользователя в домене Windows.
Если такой подход не имеет смысла, то ответ в большой степени зависит от конкретного приложения. В идеальном мире нужно было бы применить сильный протокол проверки паролей (то есть протокол с нулевым знанием), например Secure Remote Password (SRP, см., но лишь немногие сейчас пользуются такими протоколами, поскольку возможны осложнения с правами на интеллектуальную собственность.
Любое решение, кроме сильного протокола, сопряжено с риском. Мы рекомендуем остановиться на том, которое оправдано в конкретной ситуации, даже если это что–то вроде «ввести пароль, вычислить его свертку и послать свертку серверу» (это все же чуть лучше, чем послать пароль серверу, который вычислит свертку сам). Но любой протокол можно использовать лишь по защищенному соединению (с помощью SSL/TLS или аналогичной технологии), после того как клиент успешно аутентифицирует сервера, следуя рекомендациям из греха 10.
Если по какой–то причине вы не можете воспользоваться протоколом типа SSL/TLS и не хотите рисковать, применяя сильный протокол проверки паролей, то настоятельно советуем обратиться к профессиональному криптографу. В противном случае риск нарваться на неприятности очень высок!
Рекомендации по переустановке паролей
Блокировать пользователя за слишком большое число неудачных попыток ввести пароль – значит напрашиваться на DoS–атаку. Типичный пользователь в конце концов решит, что забыл пароль, и обратится к имеющейся процедуре переустановки пароля.
Вместо этого введите какое–нибудь разумное ограничение, скажем, 50 попыток входа в час, и обнаруживайте атаки на основе предположения, что законный пользователь вряд ли станет входить в систему так часто.
Альтернативное решение, дающее тот же эффект, – замедлять процедуру аутентификации после нескольких неудачных попыток. Например, обнаружив три неудачные попытки входа за короткий промежуток времени, вы можете задержать отправку сообщений сервером, так чтобы на завершение протокола аутентификации уходило десять секунд (когда атака прекратится, можно восстановить нормальную обработку).
Но это эффективно лишь в том случае, когда вы ограничиваете число одновременных попыток входа. Разумно ввести ограничение «не больше одного входа за раз». На самом деле без такого ограничения нельзя доказать корректность даже самых сильных протоколов.
Мы рекомендуем сделать борьбу с атаками частью стандартных рабочих процедур. Например, можно вести черный список IP–адресов на уровне сети. Кроме того, если зарегистрировано много попыток войти от имени некоторой учетной записи, то можно при следующем входе известить об этом ее владельца и предложить ему сменить пароль.
Если пользователь решит переустановить свой пароль, сделайте эту процедуру максимально усложненной или даже невозможной для человека. Разрешать это следует лишь тогда, когда пользователь сможет убедительно подтвердить свою личность, предъявив один или несколько предметов, которыми только он может обладать, например паспорт или копию водительских прав.
Да, пароли часто забывают, поэтому автоматизированная переустановка должна быть простой. Хотя у электронной почты есть много недостатков с точки зрения безопасности, но дать возможность получать временный, случайно сгенерированный пароль по почте все же куда лучше, чем создавать ситуацию, в которой пользователь может пасть жертвой социальной инженерии. Конечно, риск есть, особенно в корпоративной локальной сети, где чужую почту можно просмотреть.