В этой статье я предлагаю альтернативный, более сложный способ аутентификации, при котором пароль ни в чистом виде, ни в виде хэша не светится, а заодно такой подход практически исключит необходимость в добавлении captcha к форме входа.
Всё описанное уже используется на практике, поэтому примеры будут реальные, и учитывающие контекст их использования.
Аутентификация будет проходить в два этапа c использованием AJAX.
1 Этап
Подготовка, отправляем на сервер sha224 хэш логина, получаем случайный хэш. JavaScript пример:
$.ajax( base_url+'/api/System/user/login', { cache : false, data : { login : hash('sha224', login) }, success : function(random_hash) { //Get random hash }, error : function() { //Error } } );
hash() — это JavaScript обертка, которая делает то же самое, что и соответствующая php функция.
Случайный хэш генерируется на сервере. PHP пример:
$random_hash = hash('sha224', microtime(true));
Случайный хэш запоминается на сервере, и связывается c логином.
2 Этап
Собственно, аутентификация. JavaScript пример (вместе c первым):
** * Login into system * * @param {string} login * @param {string} password */ function login (login, password) { $.ajax( base_url+'/api/System/user/login', { cache : false, data : { login: hash('sha224', login) }, success : function(random_hash) { if (random_hash.length == 56) { $.ajax( base_url+"/api/user/login", { cache : false, data : { login : hash('sha224', login), auth_hash : hash( 'sha512', hash('sha224', login)+hash('sha512', hash('sha512', password)+public_key)+navigator.userAgent+random_hash ) }, success : function(result) { if (result == 'reload') { location.reload(); } else { //Error } }, error : function() { //Error } } ); } else { //Error } }, error : function() { //Error } } ); }
Как можно увидеть, для пароля используется sha512 хэш. public_key — это глобальная переменная, которая содержит строчку длиной в 56 знаков, и используется как соль. Эта переменная общедоступна, но благодаря тому, что используется относительно сложный алгоритм получения хэша sha512 (дважды) — генерация радужных таблиц для каждого отдельного сайта будет достаточно сложной, дорогой и длительной процедурой, чтобы это имело большой смысл.
Имея хэш логина, мы можем найти на сервере соответствующий пароль, случайный хэш, сгенерировать соответствующий auth_hash и сравнить c тем, который был получен от пользователя. В случае совпадения заводим сессию, и обновляем страницу пользователя. Реализация обработки ошибок остается на ваше рассуждение.
Что тогда c регистрацией
Очень просто, генерировать пароль за пользователя, и отправлять на почту. Любой уважающий себя и пользователя почтовый сервис поддерживает доступ к почте по шифрованному каналу. Пользователь при желании сможет сменить пароль.
Смена пароля/восстановление
Восстановление пароля в данной схеме тоже производится на почтовый ящик.
Смену пароля можно реализовать следующим образом:
- со старого и нового паролей генерируем хэши как в примере выше:
hash('sha512', hash('sha512', password)+public_key)
- делаем посимвольный XOR обоих паролей, например, c помощью такой функции xor_string():
String.prototype.replaceAt=function(index, symbol) { return this.substr(0, index)+symbol+this.substr(index+symbol.length); }; function xor_string (string1, string2) { var len1 = string1.length, len2 = string2.length; if (len2 > len1) { var tmp = string1; string1 = string2; string2 = tmp; tmp = len1; len1 = len2; len2 = tmp; } for (var i = 0; i < len1; ++i) { var pos = i % len2; string1 = string1.replaceAt(i, String.fromCharCode(string1.charCodeAt(i) ^ string2.charCodeAt(pos))); } return string1; }
- Отправляем на сервер два хэша. Один полученный в результате xor_string, второй от хэша текущего пароля и id текущей сессии пользователя. JavaScript пример:
/** * Password changing * * @param {string} current_password * @param {string} new_password */ function change_password (current_password, new_password) { if (!current_password) { //Error return; } else if (!new_password) { //Error return; } else if (current_password == new_password) { //Error return; } current_password = hash('sha512', hash('sha512', current_password)+public_key); new_password = hash('sha512', hash('sha512', new_password)+public_key); $.ajax( base_url+'/api/System/user/change_password', { cache : false, data : { verify_hash : hash('sha224', current_password+session_id), new_password : xor_string(current_password, new_password) }, success : function(result) { if (result == 'OK') { //Success } else { //Error } }, error : function() { //Error } } ); }
- На сервере, зная что за пользователь, мы легко генерируем verify_hash. Если он совпадает c полученным от пользователя — текущий пароль был введен корректно, делаем посимвольный xor хэша строки текущего пароля пользователя, и new_password, полученного от пользователя. Таким образом мы восстанавливаем хэш нового пароля пользователя, которым и заменяем текущий. PHP пример аналога xor_string из JavaScript примера:
function xor_string ($string1, $string2) { $len1 = mb_strlen($string1); $len2 = mb_strlen($string2); if ($len2 > $len1) { list($string1, $string2, $len1, $len2) = [$string2, $string1, $len2, $len1]; } for ($i = 0; $i < $len1; ++$i) { $pos = $i % $len2; $string1[$i] = chr(ord($string1[$i]) ^ ord($string2[$pos])); } return $string1; }
Дополнительные меры безопасности
Дополнительно рекомендую добавлять в запрос id текущей сессии пользователя, и при его отсутствии или не совпадении c текущей сессией обнулять данные POST запроса.
На этом всё
Код можете свободно использовать в своих целях, менять типы и размеры хэшей для усложнения или ускорения процедуры.
Буду рад замечаниям/пожеланиям, спасибо что дочитали до этого места.
ссылка на оригинал статьи http://habrahabr.ru/post/171347/
Добавить комментарий