Безопасный метод авторизации на PHP
Давайте посмотрим вокруг: форумы, интернет магазины, гостевые книги и т.д. используют регистрацию и последующую авторизацию пользователей. Можно даже сказать, что это почти необходимая функция каждого сайта (только если это не домашняя страничка Васи Пупкина или не визитная карточка, какой-нибудь небольшой компании). Сегодня я хочу поделиться со всеми новичками информацией, о том, как лучше это все реализовать.
1. Модель (клиент) Регистрация — логин (a-z0-9) — пароль Вход — логин — пароль Cookie — уникальный идентификатор юзера — хэш
Модель (сервер) MySQL Таблица users user_id (int(11)) user_login (Varchar(30)) user_password (varchar(32)) user_hash (varchar(32)) user_ip (int(10)) по умолчанию 0
При регистрации в базу данных записываеться логин пользователя и пароль(в двойном md5 шифровании)
При авторизация, сравниваеться логин и пароль, если они верны, то генерируеться случайная строка, которая хешируеться и добавляеться в БД в строку user_hash. Также записываеться IP адрес пользователя(но это у нас будет опциональным, так как кто-то сидит через Proxy, а у кого-то IP динамический… тут уже пользователь сам будет выбирать безопасность или удобство). В куки пользователя мы записываем его уникальный индетификатор и сгенерированный hash.
Почему надо хранить в куках хеш случайно сгенерированной строки, а не хеш пароля? 1. Из-за невнимательности программиста, во всей системе могут быть дырки, воспользовавшийсь этими дырками, злоумышленик может вытащить хеш пароля из БД и подставить его в свои куки, тем самым получить доступ к закрытым данным. В нашем же случае, двойной хеш пароля не чем не сможет помочь хакеру, так как расшифровать он его не сможет(теоретически это возможно, но на это он потратит не один месяц, а может быть и год) а воспользоваться этим хешем ему негде, ведь у нас при авторизации свой уникальный хеш прикрепленный к IP пользователя.
2. Если злоумышленик вытащит трояном у пользователя уникальный хеш, воспользовать им он также не сможет(разве если только, пользователь решил принебречь своей безопастностью и выключил привязку к IP при авторизации).
2. Практика -- <br> -- Структура таблицы `users` <br> -- <br> CREATE TABLE `users` ( <br> `user_id` int(11) unsigned NOT NULL auto_increment, <br> `user_login` varchar(30) NOT NULL, <br> `user_password` varchar(32) NOT NULL, <br> `user_hash` varchar(32) NOT NULL, <br> `user_ip` int(10) unsigned NOT NULL default '0', <br> PRIMARY KEY (`user_id`) <br> ) ENGINE=MyISAM DEFAULT CHARSET=cp1251 AUTO_INCREMENT=1 ; <br>
// Страница регситрации нового пользователя
# Соединямся с БД
mysql_connect ( "localhost" , "myhost" , "myhost" );
if(isset( $_POST [ 'submit' ]))
if(! preg_match ( "/^[a-zA-Z0-9]+$/" , $_POST [ 'login' ]))
$err [] = "Логин может состоять только из букв английского алфавита и цифр" ;
if( strlen ( $_POST [ 'login' ]) < 3 or strlen ( $_POST [ 'login' ]) > 30 )
$err [] = "Логин должен быть не меньше 3-х символов и не больше 30" ;
# проверяем, не сущестует ли пользователя с таким именем
$query = mysql_query ( "SELECT COUNT(user_id) FROM users WHERE user_login='" . mysql_real_escape_string ( $_POST [ 'login' ]). "'" );
if( mysql_result ( $query , 0 ) > 0 )
$err [] = "Пользователь с таким логином уже существует в базе данных" ;
# Если нет ошибок, то добавляем в БД нового пользователя
if( count ( $err ) == 0 )
$login = $_POST [ 'login' ];
# Убераем лишние пробелы и делаем двойное шифрование
$password = md5 ( md5 ( trim ( $_POST [ 'password' ])));
mysql_query ( "INSERT INTO users SET user_login='" . $login . "', user_password='" . $password . "'" );
header ( "Location: login.php" ); exit();
print "<b>При регистрации произошли следующие ошибки:</b><br>" ;
foreach( $err AS $error )
print $error . "<br>" ;
Логин <input name="login" type="text"><br>
Пароль <input name="password" type="password"><br>
<input name="submit" type="submit" value="Зарегистрироваться">
# Функция для генерации случайной строки
function generateCode ( $length = 6 )
<br>Пароль <input name="password" type="password"><br>
Не прикреплять к IP(не безопасно) <input type="checkbox" name="not_attach_ip"><br>
<input name="submit" type="submit" value="Войти">
# Соединямся с БД
mysql_connect ( "localhost" , "myhost" , "myhost" );
if (isset( $_COOKIE [ 'id' ]) and isset( $_COOKIE [ 'hash' ]))
$query = mysql_query ( "SELECT *,INET_NTOA(user_ip) FROM users WHERE user_id = '" . intval ( $_COOKIE [ 'id' ]). "' LIMIT 1" );
$userdata = mysql_fetch_assoc ( $query );
if(( $userdata [ 'user_hash' ] !== $_COOKIE [ 'hash' ]) or ( $userdata [ 'user_id' ] !== $_COOKIE [ 'id' ])<br> or (( $userdata [ 'user_ip' ] !== $_SERVER [ 'REMOTE_ADDR' ]) and ( $userdata [ 'user_ip' ] !== "0" )))
setcookie ( "id" , "" , time () - 3600 * 24 * 30 * 12 , "/" );
setcookie ( "hash" , "" , time () - 3600 * 24 * 30 * 12 , "/" );
print "Хм, что-то не получилось" ;
print "Привет, " . $userdata [ 'user_login' ]. ". Всё работает!" ;
print "Включите куки" ;
Для защиты формы логина от перебора, можно использовать <a href=«captcha.ru target=»_blank">капчу.
Хочу отметить, что здесь я рассматривал авторизацию основоную на cookies, не стоит в комментариях кричать, что сессии лучше/удобнее и т.д. Спасибо.