igortik
Новичок
Поругайте авторизацию и межстраничную валидацию пользователя.
Зада типичная - создать систему авторизации для сайта с возможностью дальнейшей поддержки активности "соединения" клиента и сервера.
Работает на COOKIE.
Server:
Register_globals = off.
Реализация:
- Таблица users для хранения id, name, status, blocked, ip, x_ip (другие параметры я опускаю, используются в личных целях)
-- status и blocked - активность аккаунта и статус блокировки соответственно
- Таблица users_login_session для хранения user_id, hash, ip, x_ip, last_login
-- hash - уникальный MD5 хеш, полученный конкатенацией текущего времени, ID пользователя и имени
Скрипты.
1. Авторизация
2. Скрипт проверки валидности соединения
3. Выход из системы (придумал на ходу пока писал контент этой темы
)
Видимые мною недостатки:
1. Постоянно общается одним запросом с базой для проверки валидности соединения
2. Куки могут быть украдены и защита не сработает (по вине пользователя)
Преимущества:
1. Адекватная реакция на блокировку аккаунта
2. Нет постоянного открытия сессий
3. Hash всегда уникален в базе и принадлежит тому, кто правильно ввел комбинацию логин-пароль
4. Повторный логин в системе обнуляет hash в `users_login_session`, что припятствует укравшему куку, залогиниться.
(new) 5. При выходе пользователем из аккаунта шансов войти у злоумышленника, укравшего куки - нет
Сразу же вопрос:
Валидация активного соединения определяется по конечному значению переменной $session_valid и в зависимости от ее значения пользователю открываются функции!
Допустим ли такой метод? Если нет, - прошу обосновать. Может вместо переменной использовать константу для гибкости работы функций?
В общем.. Ваши замечания ...
-~{}~ 23.06.09 13:10:
P.S. Уже вижу недочет с вылогиниванием, т.к. нам надо как-то передавать переменную $session_valid.
Sessions maybe ?
Зада типичная - создать систему авторизации для сайта с возможностью дальнейшей поддержки активности "соединения" клиента и сервера.
Работает на COOKIE.
Server:
Register_globals = off.
Реализация:
- Таблица users для хранения id, name, status, blocked, ip, x_ip (другие параметры я опускаю, используются в личных целях)
-- status и blocked - активность аккаунта и статус блокировки соответственно
- Таблица users_login_session для хранения user_id, hash, ip, x_ip, last_login
-- hash - уникальный MD5 хеш, полученный конкатенацией текущего времени, ID пользователя и имени
Скрипты.
1. Авторизация
PHP:
if($_POST['doLogin'])
{
//Входящие переменные
$ip = $_SERVER['REMOTE_ADDR'];
$x_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
$remindme = 1; // Необходимо включить в настройки
$login = mysql_real_escape_string($_POST['login']);
$password = $_POST['password']; //Иправлено. Благодарности *****'у. Экранировать нельзя, т.к. получим иной хеш в случае с паролем типа: er'drgk09.99
//-------------------------------------------------------
$search_user = mysql_query("SELECT `id`,`name`,`status`,`blocked` FROM `users` WHERE `login`='$login' AND `pass`='".md5($password)."' LIMIT 1") or die(mysql_error());
if(mysql_num_rows($search_user) == 1)
{
//echo 'Пользователь существует';
$user_id = mysql_result($search_user,0,'id');
$user_name = mysql_result($search_user,0,'name');
$status = mysql_result($search_user,0,'status');
$blocked = mysql_result($search_user,0,'blocked');
if($blocked == 1) {header("Location: ".$_SERVER['PHP_SELF']."?mod=say&message=11"); exit;} //Заблокирован
if($status == 0) {header("Location: ".$_SERVER['PHP_SELF']."?mod=say&message=12"); exit;} //Не активирован
//Создаем сессию пользователя в базе
$current_time = time();
/* Генерируем уникальный hash
Данный хеш запишется в куку для дальнейшей проверки!
Из кук он будет сопоставляться с текущим значением в таблице `users_login_sessions` для конкретного ID пользователя.
В случае подмены хеша валидация не пройдет.
*/
$user_hash = md5($current_time.$user_id.$user_name);
mysql_query("UPDATE `users_login_session` SET `hash`='$user_hash',`ip`='$ip',`x_ip`='$x_ip' WHERE `user_id`='$user_id'") or die(mysql_error());
//----------------------------------
//Запомнить на компьютере (в COOKIE)
if($remindme == 1) //переименовать переменную для эстетики
{
setcookie('user_id',$user_id,time() + (3600 * 24 * 365),"/");
setcookie('user_hash',$user_hash,time() + (3600 * 24 * 365),"/");
}
else
{
setcookie('user_id',$user_id,time() + 3600,"/");
setcookie('user_hash',$user_hash,time() + 3600,"/");
}
//-----------------------------------
}
}
PHP:
if((!isset($_COOKIE['user_id'])) && (!isset($_COOKIE['user_hash'])))
{
$session_valid = 0;
}
else {
//Обрабатываем данные из куки
$user_id = intval($_COOKIE['user_id']);
$user_hash = mysql_real_escape_string($_COOKIE['user_hash']);
}
if($user_id != 0)
{
//echo 'user_hash: '.$_COOKIE['user_hash']."<br>";
//При успешной подмене значений кук пользователь прийдет к этой стадии!
//Сравниваем полученный из кук хеш с тем, который существует в данный момент в таблице сессий для конкретного ID пользователя.
//Так как хеш уникален в базе и генерируется только п офакту ввода верного пароля, то можно предположить, что на лицо подмена куки в случае неудачи.
$find_user = mysql_query("SELECT users.id,users.name,users_login_session.hash FROM `users`
INNER JOIN `users_login_session` ON users_login_session.user_id = users.id WHERE `user_id`='$user_id' LIMIT 1") or die(mysql_error());
if(mysql_num_rows($find_user) == 1)
{
$db_user_hash = mysql_result($find_user,0,'hash');
//echo 'DB_hash: '.$db_user_hash."<br>";
/* Проверяем hash
Хеш может отличаться только в том случае, если намеренно были изменены куки
*/
if($_COOKIE['user_hash'] == $db_user_hash)
{
//Сессия активна
echo 'Сессия активна';
$session_valid = 1;
}
else
{
//Закрываем сессию (подмена куки)
echo 'Сессия закрыта';
$session_valid = 0;
}
}
}

PHP:
//Я использую эту конструкцию всегда на своих проектах для удобства управления действиями
switch($_GET['do'])
{
case logout:
//Только в случае валидности текущего соединения мы можем его разорвать
if($session_valid == 1)
{
setcookie('user_id','',time() - 3600,"/");
setcookie('user_hash','',time() - 3600,"/");
mysql_query("UPDATE `users_login_session` SET `hash`='' WHERE `user_id`='".intval($_COOKIE['user_id'])."'") or die(mysql_error());
//В данном случае соединение разрывается полностью. Даже для тех, кто украл куку, т.к. хеш обнуляется и скрипт проверки подлинности вернет FALSE
}
break;
}
1. Постоянно общается одним запросом с базой для проверки валидности соединения
2. Куки могут быть украдены и защита не сработает (по вине пользователя)
Преимущества:
1. Адекватная реакция на блокировку аккаунта
2. Нет постоянного открытия сессий
3. Hash всегда уникален в базе и принадлежит тому, кто правильно ввел комбинацию логин-пароль
4. Повторный логин в системе обнуляет hash в `users_login_session`, что припятствует укравшему куку, залогиниться.
(new) 5. При выходе пользователем из аккаунта шансов войти у злоумышленника, укравшего куки - нет
Сразу же вопрос:
Валидация активного соединения определяется по конечному значению переменной $session_valid и в зависимости от ее значения пользователю открываются функции!
Допустим ли такой метод? Если нет, - прошу обосновать. Может вместо переменной использовать константу для гибкости работы функций?
В общем.. Ваши замечания ...
-~{}~ 23.06.09 13:10:
P.S. Уже вижу недочет с вылогиниванием, т.к. нам надо как-то передавать переменную $session_valid.
Sessions maybe ?