Сервер в кармане, или просто о сложном!

главная - Статьи - WWW (HTML, PHP и др.)



Сессии. Скрипт авторизации. Часть 2

Принцип защиты, описанный в предыдущей статье, первоначально был написан без использования баз данных mysql. Первоначально в скрипте использовались файлы для хранения паролей, поэтому, переходя на базы данных я упустил возможность mysql injection. Что же, постараюсь в этой статье описать, как защититься от этой самой "инъекции". Кроме того, в статье будет описано, как решить те самые проблемы, что были обозначены в первой части.

Итак, обеспечиваем безопасную запись в базу. Самый простой способ - использование функции addslashes() - она отменит значение символов, которые могут вызывать проблемы при сохранении в базе данных. Чтобы вернуть данные в исходный вид нужно воспользоваться функцией stripslashes().

Вот таким образом это можно сделать в нашем случае:

<?php
function check()
{
    if (empty($pass) || empty($name)) error("Не указан логин или пароль");    
    //-------------начало нового кода
    $name = addslashes($name);
    //-------------конец нового кода
    $sql="select * from my_site where login='".$name."'";
    if ($show=mysql_query($sql))
    {
        $a=mysql_fetch_array($show);
        if ($a['pass'] != $pass) error("Неверное сочетание логин - пароль");
        else session_register("name","pass");
    }
    else error("Ошибка запроса к базе данных");
}
?>

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

Про регулярные выражения написано уже множество материала, поэтому опустим этот вопрос.

Еще один способ - использовать специальные функции, которые мнемонизируют (делают удобоваримой для mysql_query) строку. Это mysql_real_escape_string() и mysql_escape_string(). Только помните, что они не мнемонизирует символы % и _. Пример использования:

$name = mysql_escape_string($name);

Любой из этих способов защитит вас от mysql injection.

Идем дальше

Для более мощной защиты захешируем наши пароли.

Для этого будем использовать функцию crypt().

"Захешируем" - это значит превратим наш пароль в псевдослучайную строку, причем, такую, что ее нельзя перевести обратно.

"Если нельзя расшифровать пароль, то зачем нужна такая шифровка?", спросите вы. Отвечу. Если скормить нашей функции два одинаковых слова, то на выходе будет два абсолютно одинаковых значения. Теперь понятно как важна эта функция? Мы не можем расшифровать пароль и сравнить его с вводимым, но мы можем зашифровать вводимую строку и сравнить ее с хранящейся в базе.

Т.к. пароль нельзя расшифровать, то даже если злоумышленник каким-то образом просмотрит содержимое нашей таблицы с паролями, то он увидит только захешированные строки, из которых вытянуть настоящие пароли будет крайне затруднительно. Алгоритм шифрования зависит от операционной системы.

crypt(string_1, string_2);

Первый аргумент функции - это строка, которую нужно зашифровать. Второй - необязателен, если он не предоставлен, то будет автоматически сгенерирован PHP. Но если вы его не укажите, то на одно и тоже слова функция будет выдавать вам разный результат, поэтому нам все же придется задавать и второй аргумент. Пример использования:

$pass = crypt($pass, "ph");

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

Идем еще дальше

Решим те задачи, что поставили в предыдущей статье, а именно:

  1. Возможность входить на сайт под разными именами.
  2. "Полный выход" т.е. нельзя снова зайти на сайт не авторизировавшись.

В принципе, эти две задачи сводятся к одной. И решение ее очень простое - удаление сессии. Сделаем ссылку "выход", которая будет перебрасывать нас на страницу с формой авторизации (index.php) и удалять нашу сессию.

Вот ссылка:

А этот код разместим на index.php (в самом верху страницы):

<?php
session_start();
if (!empty($exit)) session_destroy();
?>

Если определена переменная $exit, то функция session_destroy() уничтожает сессию.

Как я и обещал в прошлой статье, я расскажу, как сделать функцию вывода ошибки, да не простую, а с записью в лог и отправкой отчета об ошибке по почте.

Итак, вот код:

function error($er)
{
    Global $name, $pass;
    //объявляем глобальные переменные.
    $time = date("H:i:s");
    //присваиваем переменной $time значение времени.
    $ip = $_SERVER['REMOTE_ADDR'];
    //в переменную $ip записываем ip пользователя
    $logFileName = "logfile.log";
    //файл, где будут храниться записи об ошибках
    $fp = fopen($logFileName,'a');
    //открываем файл с атрибутом на запись в конец файла (атрибут "а")
    if (empty($name)) $name="empty";
    if (empty($pass)) $pass="empty";
    // если имя и пароль не заданы, то присваиваем переменным значения empty.
    $message = $time." Ошибка: ".$er." Имя: ".$name." Пароль: ".$pass." ip: ".$ip." Где : ".$_SERVER['PHP_SELF']."n";
    //составляем отчет
    fputs($fp, $message);
    //производим запись в файл
    fclose($fp);
    //закрываем файл
    mail("email@mail.ru", "Error", $message, "From: error_center");
    //отправляем отчет по мылу
    echo '<center><strong>Ошибка:</strong></center> '.$er;
    //выводим сообщение об ошибке пользователю
    exit(0)
}

Теперь, когда произойдет ошибка, пользователю откроется страница с сообщением "Ошибка: описание_ошибки", а вам на ящик придет подробный отчет об ошибке, этот же отчет запишется в лог файл.

Теперь сделаем блокировку подбора пароля. Для этого изменим немного функцию error():

function error($er)
{
    Global $name, $pass;
    //объявляем глобальные переменные.
    $time = date("H:i:s");
    //присваиваем переменной $time значение времени.
    $ip = $_SERVER['REMOTE_ADDR'];
    //в переменную $ip записываем ip пользователя
    $logFileName = "logfile.log";
    //файл, где будут храниться записи об ошибках
    $fp = fopen($logFileName,'a');
    //открываем файл с атрибутом на запись в конец файла (атрибут "а")
    if (empty($name)) $name="empty";
    if (empty($pass)) $pass="empty";
    // если имя и пароль не заданы, то присваиваем переменным значения empty.
    $message = $time." Ошибка: ".$er." Имя: ".$name." Пароль: ".$pass." ip: ".$ip." Где : ".$_SERVER['PHP_SELF']."n";
    //составляем отчет
    fputs($fp, $message);
    //производим запись в файл
    fclose($fp);
    //закрываем файл
    mail("email@mail.ru", "Error", $message, "From: error_center");
   //отправляем отчет по мылу
    echo '<center><strong>Ошибка:</strong></center> '.$er;
   
    //начало нового кода
    if (empty($attempt)) $attempt=1;
    else $attempt++;
    session_register("attempt");
    //конец нового кода

    //выводим сообщение об ошибке пользователю
    exit(0)
}

Немного поясню новый код: переменная $attempt является счетчиком количеств неудачных попыток попасть на сайт. Каждый раз, когда вызывается функция error(), значение переменной увеличивается на 1. Теперь подправим функцию check():

<?php
function check()
{
    //начало нового кода
    Global $attempt;
    $attempt= intval($attempt);
    if ($attempt==3)
    exit();
    //конец нового кода

    if (empty($pass) || empty($name)) error("Не указан логин или пароль");
    $pass=crypt($pass, "ph");
    $sql="select * from my_site where login='".$name."'";
    if ($show=mysql_query($sql))
    {
        $a=mysql_fetch_array($show);
        if ($a['pass'] != $pass) error("Неверное сочетание логин - пароль");
        else session_register("name","pass");
    }
    else error("Ошибка запроса к базе данных");
}
?>

Поясню новый код: функция intval получает целочисленное значение переменной, далее если количество попыток равно 3, то мы ничего больше не показываем, на какую бы страницу не заходил пользователь. Эта блокировка будет действовать до тех пор, пока не умрет сессия.

Так же можно внести блокировку и на index.php там мы напишем тоже самое:

<?php
$attempt = intval($attempt);
if ($attempt == 3)
exit();
?>

Вот и все. Теперь вряд ли кто-нибудь сможет получить доступ к вашему ресурсу без разрешения.

Автор: Александр, http://cngroup.ru/


Авторизуйтесь для добавления комментариев!


    забыли пароль?    новый пользователь?