В статье я опишу, как создать небольшой, но действенный скрипт авторизации, который не позволит непрошенным личностям совать свой длинный нос на ваш сайт. Данный пример не претендует на звание идеального метода защиты, но все же обойти его будет достаточно трудно (я, по крайней мере, не знаю, как это сделать). Представим следующее: у вас есть закрытый сайт "для своих" и вы не хотите, чтобы туда заходили всякие нехорошие личности. В этом нам помогут сессии. Пароли и логины будем хранить в таблице mysql.
На первой странице (index.php) делаем форму для ввода данных:
Думаю здесь пояснять ничего не нужно. Идем дальше:
Сначала краткое пояснение. Мы создаем функцию проверки пользователя check(). У нас есть база данных my_site, содержащая поля id, name и pass, где name и pass - логин и пароль пользователя соответственно. Функция пропускает только тех, кто указал верный логин и пароль в форме ($name, $pass) на входе, потом эти переменные сохраняются в сессию, следовательно, пока сессия жива пользователь может заходить на ваш сайт без авторизации. Даже указав другой верный логин и пароль, он будет авторизирован под указанным ранее логином.
Предполагается, что соединение с базой mysql уже произведено и база данных выбрана.
Начнем пояснения с первой строки.
Проверяем, задан ли пароль и логин. Если одно из полей пустое, то
Здесь error - это любая функция вывода ошибки. В следующей статье я подробно затрону эту тему (вывод ошибки, запись ее в лог, отправка отчета об ошибке и т.д.), а пока мы можем обойтись простой функцией:
Если поля не пусты, то скрипт продолжает работу.
Пишем запрос к базе mysql, который вернет нам значение поля pass, строки, где login=$name.
Если запрос обработан без ошибок, то
Присваиваем переменной $а (ассоциативный массив) значение поля pass
Если поле pass не соответствует тому, что ввел пользователь или тому, что хранится в сессии, то мы опять через функцию error выводим ему ошибку.
Иначе записываем переменные $name и $pass в сессию и продолжаем выполнять скрипт.
Если есть ошибка в запросе к базе mysql, то мы опять же выводим ошибку пользователю.
Итак, вначале каждой страницы, к которой нужно запретить доступ посторонним пишем:
Теперь проверим логику скрипта. Если человек не заполняет одно из полей и нажимает кнопку "вход", то перейдя на страницу index2.php он получит надпись "Ошибка: Не указан логин или пароль".
Если он указывает неверный логин или пароль, то ему выпадает "Ошибка: Не верное сочетание логин - пароль".
Если же он все указывает верно, то попадет на страницу index2.php, где вы храните свои тайны. За счет того, что логин и пароль записываются в сессию, юзер может бродить по вашему сверх тайному сайту авторизировавшись только один раз.
Возможные проблемы:
Нельзя заходить на сайт под разными именами.
Если человек раз зашел на сайт, то с его компьютера можно посещать этот сайт еще в течении некоторого времени (пока жива сессия), что не очень хорошо, если ты работаешь из Интернет салона.
Решение этих проблем будет описано в следующих статьях. Также будут затронуты вопросы: система обнаружения атак и попыток подбора пароля, с отправкой отчета по почте, функция вывода ошибок, с записью в лог файл.
Принцип защиты, описанный в предыдущей статье, первоначально был написан без использования баз данных mysql. Первоначально в скрипте использовались файлы для хранения паролей, поэтому, переходя на базы данных я упустил возможность mysql injection. Что же, постараюсь в этой статье описать, как защититься от этой самой "инъекции". Кроме того, в статье будет описано, как решить те самые проблемы, что были обозначены в первой части.
Итак, обеспечиваем безопасную запись в базу. Самый простой способ - использование функции addslashes() - она отменит значение символов, которые могут вызывать проблемы при сохранении в базе данных. Чтобы вернуть данные в исходный вид нужно воспользоваться функцией stripslashes().
Вот таким образом это можно сделать в нашем случае:
Второй способ - использование регулярных выражений. С помощью регулярных выражений вы уже сами можете решать - какие символы допускать, а какие нет.
Про регулярные выражения написано уже множество материала, поэтому опустим этот вопрос.
Еще один способ - использовать специальные функции, которые мнемонизируют (делают удобоваримой для mysql_query) строку. Это mysql_real_escape_string() и mysql_escape_string(). Только помните, что они не мнемонизирует символы % и _. Пример использования:
Любой из этих способов защитит вас от mysql injection.
Идем дальше
Для более мощной защиты захешируем наши пароли.
Для этого будем использовать функцию crypt().
"Захешируем" - это значит превратим наш пароль в псевдослучайную строку, причем, такую, что ее нельзя перевести обратно.
"Если нельзя расшифровать пароль, то зачем нужна такая шифровка?", спросите вы. Отвечу. Если скормить нашей функции два одинаковых слова, то на выходе будет два абсолютно одинаковых значения. Теперь понятно как важна эта функция? Мы не можем расшифровать пароль и сравнить его с вводимым, но мы можем зашифровать вводимую строку и сравнить ее с хранящейся в базе.
Т.к. пароль нельзя расшифровать, то даже если злоумышленник каким-то образом просмотрит содержимое нашей таблицы с паролями, то он увидит только захешированные строки, из которых вытянуть настоящие пароли будет крайне затруднительно. Алгоритм шифрования зависит от операционной системы.
Первый аргумент функции - это строка, которую нужно зашифровать. Второй - необязателен, если он не предоставлен, то будет автоматически сгенерирован PHP. Но если вы его не укажите, то на одно и тоже слова функция будет выдавать вам разный результат, поэтому нам все же придется задавать и второй аргумент. Пример использования:
Таким образом, мы хешируем вводимый пользователем пароль и сверяем полученный результат с табличным значением (а там хранятся захешированные с тем же вторым аргументом пароли), если значения совпадают, значит впускаем пользователя.
Идем еще дальше
Решим те задачи, что поставили в предыдущей статье, а именно:
В принципе, эти две задачи сводятся к одной. И решение ее очень простое - удаление сессии. Сделаем ссылку "выход", которая будет перебрасывать нас на страницу с формой авторизации (index.php) и удалять нашу сессию.
Вот ссылка:
А этот код разместим на index.php (в самом верху страницы):
Если определена переменная $exit, то функция session_destroy() уничтожает сессию.
Как я и обещал в прошлой статье, я расскажу, как сделать функцию вывода ошибки, да не простую, а с записью в лог и отправкой отчета об ошибке по почте.
Итак, вот код:
Теперь, когда произойдет ошибка, пользователю откроется страница с сообщением "Ошибка: описание_ошибки", а вам на ящик придет подробный отчет об ошибке, этот же отчет запишется в лог файл.
Теперь сделаем блокировку подбора пароля. Для этого изменим немного функцию error():
Немного поясню новый код: переменная $attempt является счетчиком количеств неудачных попыток попасть на сайт. Каждый раз, когда вызывается функция error(), значение переменной увеличивается на 1. Теперь подправим функцию check():
Поясню новый код: функция intval получает целочисленное значение переменной, далее если количество попыток равно 3, то мы ничего больше не показываем, на какую бы страницу не заходил пользователь. Эта блокировка будет действовать до тех пор, пока не умрет сессия.
Так же можно внести блокировку и на index.php там мы напишем тоже самое:
Вот и все. Теперь вряд ли кто-нибудь сможет получить доступ к вашему ресурсу без разрешения.