Как измерить надежность пароля?
Я искал эффективный алгоритм, который мог бы дать мне точное представление о том, насколько надежен пароль.
Я обнаружил, что несколько разных сайтов используют несколько разных алгоритмов, так как я получаю разные оценки надежности пароля на разных сайтах.
6 ответов
Это переросло в мой общий мозг лучших рекомендаций по работе с паролями в PHP/MySQL. Представленные здесь идеи, как правило, не мои, но лучшие из того, что я нашел на сегодняшний день.
Убедитесь, что вы используете SSL для всех операций, связанных с пользовательской информацией. Все страницы, содержащие эти формы, должны проверять, что они вызываются через HTTPS, и отказываться работать в противном случае.
Вы можете устранить большинство атак, просто ограничив количество неудачных входов в систему.
Разрешить относительно слабые пароли, но хранить количество неудачных входов в систему на пользователя и требовать проверки или проверки пароля по электронной почте, если вы превысите его. Я установил мои максимальные сбои на 5.
Сообщение о сбоях при входе в систему необходимо тщательно продумать, чтобы не предоставлять информацию злоумышленникам. Неудачный вход в систему из-за несуществующего пользователя должен возвращать то же сообщение, что и неудачный вход в систему из-за неверного пароля. Предоставление другого сообщения позволит злоумышленникам определить действительные логины пользователей.
Также убедитесь, что вы возвращаете одно и то же сообщение в случае сбоя при слишком большом количестве входов в систему с действительным паролем и сбоя при слишком большом количестве входов в систему и неверного пароля. Предоставление другого сообщения позволит злоумышленникам определить действительные пароли пользователей. Значительное количество пользователей, когда им придется сбросить свой пароль, просто вернут его на прежний уровень.
К сожалению, ограничение количества входов в систему на IP-адрес нецелесообразно. Несколько провайдеров, таких как AOL и большинство компаний, используют свои веб-запросы для прокси. Введение этого ограничения эффективно устранит этих пользователей.
Я обнаружил, что проверка словарных слов перед отправкой неэффективна, так как нужно либо отправить словарь клиенту в javascript, либо отправить ajax-запрос на изменение поля. Я делал это некоторое время, и это работало нормально, но мне не нравился трафик, который он генерировал.
Проверка на наличие слабых по своей сути паролей минус слова из словаря - это практическая клиентская часть с небольшим простым javascript.
После отправки я проверяю слова словаря и имя пользователя, содержащее пароль, и наоборот на стороне сервера. Очень хорошие словари легко загружаются, и тестирование на них просто. Здесь есть один недостаток: для проверки слова из словаря необходимо отправить запрос к базе данных, которая снова содержит пароль. Способ, которым я смог обойти это, заключался в том, чтобы зашифровать свой словарь заранее, используя простое шифрование и SALT в конечной позиции, а затем проверить зашифрованный пароль. Не идеально, но лучше, чем обычный текст, и только по проводам для людей на ваших физических машинах и в подсети.
Как только вы довольны паролем, они выбрали сначала зашифровать его с помощью PHP, а затем сохранить. Следующая функция шифрования паролей тоже не моя идея, но решает ряд проблем. Шифрование в PHP не позволяет людям на общем сервере перехватывать ваши незашифрованные пароли. Добавление чего-либо для пользователя, которое не изменится (я использую электронную почту, так как это имя пользователя для моих сайтов), и добавление хэша (SALT - это короткая постоянная строка, которую я изменяю для каждого сайта) повышает устойчивость к атакам. Поскольку ОСВ находится внутри пароля, а пароль может быть любой длины, практически невозможно атаковать его с помощью радужного стола. С другой стороны, это также означает, что люди не могут изменить свою электронную почту, а вы не можете изменить ОСВ, не аннулировав пароль каждого.
РЕДАКТИРОВАТЬ: Я бы сейчас рекомендовал использовать PhPass вместо моей собственной функции здесь, или просто забыть логины пользователей и использовать вместо них OpenID.
function password_crypt($email,$toHash) {
$password = str_split($toHash,(strlen($toHash)/2)+1);
return hash('sha256', $email.$password[0].SALT.$password[1]);
}
Мой Jqueryish измеритель пароля на стороне клиента. Цель должна быть div. Его ширина будет меняться от 0 до 100, а цвет фона будет меняться в зависимости от классов, обозначенных в скрипте. Опять в основном украденные из других вещей, которые я нашел:
$.updatePasswordMeter = function(password,username,target) {
$.updatePasswordMeter._checkRepetition = function(pLen,str) {
res = ""
for ( i=0; i<str.length ; i++ ) {
repeated=true;
for (j=0;j < pLen && (j+i+pLen) < str.length;j++)
repeated=repeated && (str.charAt(j+i)==str.charAt(j+i+pLen));
if (j<pLen) repeated=false;
if (repeated) {
i+=pLen-1;
repeated=false;
}
else {
res+=str.charAt(i);
};
};
return res;
};
var score = 0;
var r_class = 'weak-password';
//password < 4
if (password.length < 4 || password.toLowerCase()==username.toLowerCase()) {
target.width(score + '%').removeClass("weak-password okay-password good-password strong-password"
).addClass(r_class);
return true;
}
//password length
score += password.length * 4;
score += ( $.updatePasswordMeter._checkRepetition(1,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(2,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(3,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(4,password).length - password.length ) * 1;
//password has 3 numbers
if (password.match(/(.*[0-9].*[0-9].*[0-9])/)) score += 5;
//password has 2 symbols
if (password.match(/(.*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~])/)) score += 5;
//password has Upper and Lower chars
if (password.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/)) score += 10;
//password has number and chars
if (password.match(/([a-zA-Z])/) && password.match(/([0-9])/)) score += 15;
//
//password has number and symbol
if (password.match(/([!,@,#,$,%,^,&,*,?,_,~])/) && password.match(/([0-9])/)) score += 15;
//password has char and symbol
if (password.match(/([!,@,#,$,%,^,&,*,?,_,~])/) && password.match(/([a-zA-Z])/)) score += 15;
//password is just a nubers or chars
if (password.match(/^\w+$/) || password.match(/^\d+$/) ) score -= 10;
//verifing 0 < score < 100
score = score * 2;
if ( score < 0 ) score = 0;
if ( score > 100 ) score = 100;
if (score > 25 ) r_class = 'okay-password';
if (score > 50 ) r_class = 'good-password';
if (score > 75 ) r_class = 'strong-password';
target.width(score + '%').removeClass("weak-password okay-password good-password strong-password"
).addClass(r_class);
return true;
};
Принципиально вы хотите предотвратить основные виды атак
- Словарные атаки
- Атаки грубой силой
Чтобы предотвратить первое, вы хотите считать пароли, содержащие общие слова, слабыми. Чтобы предотвратить второе, вы должны поощрять пароли разумной длины (обычно более 8 символов) и с достаточно большим набором символов (включая буквы, цифры и специальные символы). Если вы считаете, что строчные и прописные буквы различны, это существенно увеличивает набор символов. Однако это создает проблему юзабилити для некоторых сообществ пользователей, поэтому вам необходимо сбалансировать это соображение.
Быстрый поиск в Google позволил найти решения, которые учитывают атаки методом перебора (сложный пароль), но не атаки по словарю. PHP Password Strength Meter из этого списка проверок надежности выполняет проверку на стороне сервера, поэтому он может быть расширен для проверки словаря.
РЕДАКТИРОВАТЬ:
Кстати... вы также должны ограничить количество попыток входа в систему на пользователя. Это сделает оба типа атак менее вероятными. Эффективным, но не удобным для пользователя является блокирование учетной записи после X неудачных попыток и требует сброса пароля. Более удобный, но больше усилий - сократить время между попытками входа в систему. Вы также можете потребовать CAPTCHA после первых нескольких попыток входа в систему (что требуется для переполнения стека после слишком большого количества изменений или для совсем новых пользователей).
В основном вы, вероятно, хотите использовать регулярные выражения для проверки длины и сложности пароля.
Хороший пример, делающий это, используя JavaScript, можно найти здесь:
http://marketingtechblog.com/programming/javascript-password-strength/
Как отметил Дарен Швенке, вам лучше работать над безопасностью самостоятельно, а не отдавать это в руки пользователя.
Но хорошо бы дать пользователю несколько подсказок о том, насколько надежен его пароль, потому что лучший способ получить пароль - это по-прежнему социальное развитие.
Таким образом, вы можете взломать небольшой скрипт на стороне клиента, который в реальном времени проверяет надежность пароля пользователя в качестве индикатора вежливости. Он ничего не блокирует, но дает зеленое чувство, когда становится зеленым:-)
По сути, вы должны проверить, что это обычный смысл: проверьте, содержит ли пароль буквы, цифры и не алфавитные символы в разумных количествах.
Вы можете очень легко взломать свой собственный алгоритм: просто сделайте 10 / 10 отметку:
- 0 - пароль нулевой длины;
- +2 за каждые 8 символов в пароле (15 должна быть безопасной длины);
- +1 за использование буквы, +2 за использование 2 букв;
- +1 за использование числа, +2 за использование 2 числа;
- +1 за использование неалфавитных символов, +2 за 2.
Вам не нужно проверять богоподобные пароли (есть ли заглавные буквы, где расположены специальные символы и т. Д.), Ваши пользователи не в банке / военной / секретной службе / ежемесячной индустрии питонов, не так ли?
Вы можете написать это за час без сумасшедших навыков JavaScript.
И в любом случае, подтвердите пароль и переместите весь код безопасности на стороне сервера. Если вы можете делегировать аутентификацию (например, открытый идентификатор), даже лучше.
Не скатывайся!
Эксперты по криптографии не одобряют криптографию по собственной инициативе по причинам, которые должны быть очевидны.
По тем же причинам нельзя пытаться найти собственное решение проблемы измерения надежности пароля; это очень большая криптографическая проблема.
Не ввязывайтесь в уродливый бизнес - создавать для этой цели какие-то массивные регулярные выражения; вы, вероятно, не сможете учесть несколько факторов, которые влияют на общую надежность пароля.
Это сложная проблема
Существует значительная сложность, связанная с проблемой измерения надежности пароля. Чем больше исследований я выполняю по этому вопросу, тем больше понимаю, что это "однонаправленная" проблема; то есть нельзя измерить "сложность" (вычислительную стоимость)эффективного взлома пароля. Скорее, более эффективно предоставлять требования к сложности и измерять способность пароля соответствовать им.
Когда мы рассмотрим проблему логически, "индекс хрупкости" не имеет особого смысла, как бы это ни звучало удобно. Есть так много факторов, которые управляют вычислениями, большинство из которых относятся к вычислительным ресурсам, предназначенным для процесса взлома, что является непрактичным.
Представьте себе, что Джон Риппер(или аналогичный инструмент) сопоставляет данный пароль; На взлом подходящего пароля могут уйти дни, месяцы, чтобы взломать хороший пароль, и пока солнце не сгорит, чтобы взломать исключительный пароль. Это не практический способ измерения надежности пароля.
Подход к решению проблемы с другой стороны гораздо более управляем: если мы предоставим набор требований к сложности, можно очень быстро оценить относительную надежность пароля. Очевидно, что предъявляемые требования к сложности должны со временем изменяться, но существует гораздо меньше переменных, которые необходимо учитывать, если мы подходим к проблеме таким образом.
Жизнеспособное решение
Существует отдельная утилита, доступная от Openwall под названиемpasswdqc
(предположительно, стоит для проверки качества пароля). Разработчик Openwall, Solar Designer, действительно является добросовестным экспертом в области криптографии (его работы говорят сами за себя), и поэтому он достаточно квалифицирован, чтобы создавать такой инструмент.
Для моего конкретного варианта использования это гораздо более привлекательное решение, чем использование непродуманного фрагмента JavaScript, живущего в каком-то темном уголке Интернета.
Установление параметров для ваших конкретных потребностей является самой сложной частью. Реализация является легкой частью.
Практический пример
Я предлагаю простую реализацию в PHP, чтобы обеспечить быстрый старт. Стандартные отказ от ответственности применяются.
В этом примере предполагается, что мы вводим полный список паролей в скрипт PHP. Само собой разумеется, что если вы делаете это с реальными паролями (например, с паролями, сбрасываемыми из диспетчера паролей), следует соблюдать крайнюю осторожность в отношении обработки паролей. Простая запись незашифрованного дампа паролей на диск ставит под угрозу безопасность ваших паролей!
passwords.csv
:
"Title","Password"
"My Test Password","password123"
"Your Test Password","123456!!!"
"A strong password","NFYbCoHC5S7dngitqCD53tvQkAu3dais"
password-check.php
:
<?php
//A few handy examples from other users:
//http://php.net/manual/en/function.str-getcsv.php#117692
$csv = array_map('str_getcsv', file('passwords.csv'), [',']);
array_walk($csv, function(&$a) use ($csv) {
$a = array_combine($csv[0], $a);
});
//Remove column header.
array_shift($csv);
//Define report column headers.
$results[] = [
'Title',
'Result',
'Exit Code',
];
$i = 1;
foreach ($csv as $p) {
$row['title'] = $p['Title'];
//If the value contains a space, it's considered a passphrase.
$isPassphrase = stristr($p['Password'], ' ') !== false ? true : false;
$cmd = 'echo ' . escapeshellarg($p['Password']) . ' | pwqcheck -1 min=32,24,22,20,16 max=128';
if ($isPassphrase) {
$cmd .= ' passphrase=3';
}
else {
$cmd .= ' passphrase=0';
}
$output = null;
$exitCode = null;
$stdOut = exec($cmd, $output, $exitCode);
//Exit code 0 represents an unacceptable password (not an error).
//Exit code 1 represents an acceptable password (it meets the criteria).
if ($exitCode === 0 || $exitCode === 1) {
$row['result'] = trim($stdOut);
$row['exitCode'] = $exitCode;
}
else {
$row['result'] = 'An error occurred while calling pwqcheck';
$row['exitCode'] = null;
}
$results[$i] = $row;
$i++;
}
$reportFile = 'report.csv';
$fp = @fopen($reportFile, 'w');
if ($fp !== false) {
foreach ($results as $p) {
fputcsv($fp, $p);
}
fclose($fp);
}
else {
die($reportFile . ' could not be opened for writing (destination is not writable or file is in use)');
}
exit;
равнодействующая report.csv
:
Title,Result,"Exit Code"
"My Test Password","Bad passphrase (too short)",1
"Your Test Password","Bad passphrase (too short)",1
"A strong password",OK,0
Обертывание-Up
Я еще не нашел более тщательного решения в Интернете; Излишне говорить, что я приветствую любые другие рекомендации.
Очевидно, что этот подход не идеален для определенных случаев использования (например, "измеритель надежности пароля", реализованный "на стороне клиента"). Несмотря на это, было бы тривиально сделать AJAX-вызов к ресурсу на стороне сервера, который возвращает ответ о прохождении / сбое, используя подход, описанный выше, но такой подход должен предполагать возможность злоупотребления (например, DoS-атаки) и потребовать безопасная связь между клиентом и сервером, а также принятие рисков, связанных с передачей нехэшированного пароля.
Я не могу придумать какой-то конкретный алгоритм проверки надежности пароля. Что мы делаем, мы определяем несколько критериев, и когда пароль соответствует критерию, мы добавляем 1 к его оценке. Когда пароль достигает порогового значения, пароль надежный. В противном случае это слабый.
Вы можете определить много разных уровней прочности, если с разным порогом, или вы можете определить разные значения для конкретных критериев. Например, если пароль имеет 5 символов, мы добавляем 1, но если он получил 10, то мы добавляем 2.
вот список критериев для проверки
Длина (от 8 до 12 в порядке, чем больше, тем лучше). Содержит строчную букву. Содержит прописную букву. Заглавная буква НЕ является первой. Содержит число. Содержит символы. Последний символ НЕ является символом, подобным человеку (например,.!!) И не похож на словарное слово. В некоторых хитрых паролях есть библиотека заменителей слов и букв (например, Библиотека -> L1br@ry).
Надеюсь, что поможет.