PHP в деталях

       

Пароль на страницу. Часть 4. Печенюшки


DL
26.6.2001

Способ этот применим там, где, во-первых, пользователей много, и их контингент постоянно меняется. Во-вторых, где нужно сделать удобный вход - чтобы можно было зайти в систему, введя логин и пароль в форме на странице.

Рисуем форму и делаем файл, который получает логин и пароль (защиту от подборки я уде описывал, допишите её сюда сами).

// обработка строки с логином

$login = str_repalce("'", "", $login);

$login_result = mysql_query("SELECT id FROM user WHERE login='$login' AND pass='". md5($pass). "'");

if (!mysql_error() && @mysql_num_rows($login_result)==1) {

/* выдача кук. Имена кук и путь лучше во избежание путаницы определять в едином подключаемом файле. */

setcookie($COOKIE_LOGIN_NAME, $login, time()+3600, $COOKIE_PATH);

  setcookie($COOKIE_PASSW_NAME, $pass, time()+3600, $COOKIE_PATH);

/* Сразу же после входа пользователя перенаправляют на закрытый паролем адрес. */

  header("Location: /somepath/");

  exit;



  }

elseif (!mysql_error()) {

/* вывод сообщения об ошибке и формы для повторного ввода */

  print ("Неправильный логин или пароль.");

  }

else

  print (mysql_error());

Все закрытые страницы вызывают файл, в котором проверяется правильность пароля, полученного из куки:

$login = str_repalce("'", "", $HTTP_COOKIE_VARS[$COOKIE_LOGIN_NAME]);

$login_result = mysql_query("SELECT id FROM user WHERE login='$login' AND pass='". md5($HTTP_COOKIE_VARS[$COOKIE_PASSW_NAME]). "'");

if (!mysql_error() && @mysql_num_rows($login_result)!=1) {

/* Если такой строки в таблице нет, пользователь перенаправляется на страицу входа в систему. */

  header("Location: /login.php");

  exit;

  }

else

  print (mysql_error());

Имена кук будут использоваться в нескольких местах, поэтому лучше заранее поместить их в одном месте (например, объявив константы), чтобы потом не исправлять по нескольку раз.


Как видите, пароль будет бегать по каналу и лежать в файле с куками в открытом незакодированном виде. Это очень небезопасно. В отсутствие хозяина можно подойти к компьютеру, заглянуть в файл, где броузер держит куки, и записать пароль на бумажку (а если в локальной сети всё общее, то и подходить не надо, и стащить пароль можно прямо при хозяине).

Чтобы этого не произошло, пароль нужно кодировать. Как приемлемый вариант, хэш md5. Тут уже нельзя увидеть пароль и зайти в систему, записав его на бумажку или copy-paste-нув. Кстати, именно так можно залазить под паролем и без ведома друга в web-интерфейсы, строящие авторизацию на сессиях. Поэтому последнее, что можно сделать в этом направлении - это менять куку при каждой загрузке страницы.

Сам когда-то делал такую схему: в таблице пользователей есть колонка с датой последнего обращения. Эту дату последнего обращения и пароль, закодированные через md5, пользователь получает при каждом обращении. Система берёт куку с логином, вытаскивает из базы эту строку, генерирует хэш от полей last_log и passwd и сравнивает его с полученным. Если они совпадают, значит посетителя можно впускать. Для пущей безопасности можно добавить проверку на истечение куки - кука должна истечь после получаса неактивности, и, соответсвенно, в базе дата последнего лога должна быть менее чем полчаса назад.

$login = str_repalce("'", "", $HTTP_COOKIE_VARS[$COOKIE_LOGIN_NAME]);

$login_result = mysql_query("SELECT * FROM user WHERE login='$login' AND last_log>DATE_SUB(NOW(), INTERVAL 30 MINUTE)");

if (!mysql_error() && @mysql_num_rows($login_result)==1) {

/* Получаем строку таблицы и формируем хэш от нужных полей. */

  $current_user = mysql_fetch_array($login_result);

  $hash_to_check = md5($current_user["passwd"]. " Ы - чтоб никто не догадался ". $current_user[log_time]);

  if ($hash_to_check == $HTTP_COOKIE_VARS[$COOKIE_HASH_NAME]) {

    $current_time = time();



/* Обновление поля последнего входа и выдача новой куки. */

    mysql_query("UPDATE user SET last_log='". date("Y-m-d H:i:s", $current_time). "' WHERE login='$login'");

    setcookie($COOKIE_HASH_NAME, md5(date("Y-m-d H:i:s", $current_time). " Ы - чтоб никто не догадался ". $current_user["passwd"]), $current_time + 1800, $COOKIE_PATH);

    }

  else {

/* В случае несовпадения хэша пользователь перенаправляется на страицу входа в систему. */

    header ("Location: /login.php");

    exit;

    };

  }

elseif (!mysql_error() && @mysql_num_rows($log_result)!=1) {

  header("Location: /login.php");

  exit;

  }

else

  print (mysql_error());

Разумеется, " Ы - чтоб никто не догадался " лучше тоже выделить в отдельную переменную, а лучше использовать вместо этой строки ip-адрес посетителя (или, для обрывающегося диалапа, первые два/три числа ip-адреса).

Кстати, насчёт IP-адреса. Его лучше проверять, но не весь адрес, а только первые два (для ip, начинающихся на число меньше 127) или три (соответственно, больше 127) числа адреса. Это спасёт пользователей плохого и обрывающегося диалапа от необходимости заново авторизовыватсья после обрыва связи, и в то же время, не даст зайти взломщику, укравшему куку. Конечно же, он не сможет перезвонить и зайти через другого провайдера - адрес пула не тот, но это не наши проблемы ("в такую погоду свои дома сидят"). Как не наша проблема и воровство паролей внутри фирмы. Мы защитили от любопытных товарищей и неграмотных взломщиков, а против троянов и снифферов, которые можно поставить жертве, ничего сделать не можем.

На этом "навороты" закончились. Надёжнее защиту уже не сделать. Никто не будет лазить в файл кук за хэшем и подбирать его. Проще будет поместить между пользователем и веб-интерфейсом сниффер и при помощи него найти пароль. Можно поместить трояна, который будет запоминать всё, что пользователь ввёл на клавиатуре, но это уже не наши проблемы. Чтобы защититься от прослушивания канала, надо использовать соединения типа SSL или шифрование данных.


Содержание раздела