- PVSM.RU - https://www.pvsm.ru -
Сегодня все ещё большинство сайтов работает по протоколу http, а не https. Это хуже в плане безопасности, но дешевле и проще в поддержке. Безопасность аутентификации на таких сайтах — проблема, так как чаще всего она реализуется простой отправкой формы c логином/паролем на сервер, где хэш пароля сверяется c хэшем, который соответствует указанному логину. Входя на такой сайт в открытой Wi-Fi сети или другом публичном месте любой кто умеет пользоваться google сможет без особых усилий перехватить ваш пароль. Некоторые идут чуть дальше, отправляя на сервер хэш пароля, но и это не на много лучше так как имея радужную таблицу (особенно для md5) можно попробовать найти пароль, имея не нулевой на успех, и каждый день вероятность успеха растет.
В этой статье я предлагаю альтернативный, более сложный способ аутентификации, при котором пароль ни в чистом виде, ни в виде хэша не светится, а заодно такой подход практически исключит необходимость в добавлении captcha к форме входа.
Всё описанное уже используется на практике, поэтому примеры будут реальные, и учитывающие контекст их использования.
Аутентификация будет проходить в два этапа c использованием AJAX.
Подготовка, отправляем на сервер 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 логином.
Собственно, аутентификация. 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 тем, который был получен от пользователя. В случае совпадения заводим сессию, и обновляем страницу пользователя. Реализация обработки ошибок остается на ваше рассуждение.
Очень просто, генерировать пароль за пользователя, и отправлять на почту. Любой уважающий себя и пользователя почтовый сервис поддерживает доступ к почте по шифрованному каналу. Пользователь при желании сможет сменить пароль.
Восстановление пароля в данной схеме тоже производится на почтовый ящик.
Смену пароля можно реализовать следующим образом:
hash('sha512', hash('sha512', password)+public_key)
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;
}
/**
* 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
}
}
);
}
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 запроса.
Код можете свободно использовать в своих целях, менять типы и размеры хэшей для усложнения или ускорения процедуры.
Буду рад замечаниям/пожеланиям, спасибо что дочитали до этого места.
Автор: nazarpc
Источник [1]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/28574
Ссылки в тексте:
[1] Источник: http://habrahabr.ru/post/171347/
Нажмите здесь для печати.