Это выросло до моего общего умственного опыта по работе с паролями в 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;
};