Пишем свою авторизацию на PHP и MySQL
Ну что же, сегодня я вам расскажу о безопасной авторизации на PHP и Cookie. Ну о том что она абсолютна безопасна я не говорю, ибо взломать можно все, но для маленького сайта она вполне подходит. Так же я предвижу комментарии о том что сессии безопаснее. Не спорю куки уступают сессиям в безопасности но для реализации простенькой авторизации вполне подходят. Подробности ниже.
И так, в базе у нас будет 1 база из 4 полей: users_id, users_login, users_password и users_hash. SQL запрос:
1 2 3 4 5 6 7 | CREATE TABLE IF NOT EXISTS `users` ( `users_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `users_login` VARCHAR(30) NOT NULL, `users_password` VARCHAR(32) NOT NULL, `users_hash` VARCHAR(32) NOT NULL, PRIMARY KEY (`users_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; |
-
Сам скрипт авторизации будет тоже на 4 файлах. А именно:
- conf.php — Файл конфигурации, в котором так же содержится подключение к бд;
- register.php — Регистрация нового пользователя;
- login.php — Авторизация пользователя;
- check.php — Скрипт проверки авторизации;
Давайте разберем каждый файл.
conf.php
В константах содержится данные SQL подключения, а именно хост, логин, пароль и имя вашей базы. Тут же мы подключаемся к базе, устанавливаем кодировку в которой будут передаваться данные в бд и выбираем бд с которой будем работать. Так же тут содержится массив ошибок которые могут возникнуть при авторизации.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?php # настройки define ('DB_HOST', 'localhost'); define ('DB_LOGIN', 'example_user'); define ('DB_PASSWORD', 'example_password'); define ('DB_NAME', 'example_base'); mysql_connect(DB_HOST, DB_LOGIN, DB_PASSWORD) or die ("MySQL Error: " . mysql_error()); mysql_query("set names utf8") or die ("<br>Invalid query: " . mysql_error()); mysql_select_db(DB_NAME) or die ("<br>Invalid query: " . mysql_error()); # массив ошибок $error[0] = 'Я вас не знаю'; $error[1] = 'Включи куки'; $error[2] = 'Тебе сюда нельзя'; ?> |
register.php
Файл регистрации, тут содержится простейшая форма и ее обработчик. Исходный код прокомментирован, но общий процесс я коротко опишу. Вначале проверяем наш логин, он может содержать только английские буквы и цифры. Далее мы проверяем длину логина, от 3 до 30 символов. Проверяем свободен ли логин. При успешных проверках добавляем нового пользователя в базу. Из введенного пароля мы вырезаем пробелы на случай если пользователь хранит свои пароли в каком ни будь текстовом файле (в windows текстовые редакторы любят «хватать» пробелы в начале или конце выделяемого текста). Шифруем пароль в двойном MD5 и добавляем в базу данные о новом пользователе. Перебрасываем пользователя на login.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | <?php # Подключаем конфиг include 'conf.php'; if(isset($_POST['submit'])) { $err = array(); # проверям логин 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(users_id) FROM users WHERE users_login='".mysql_real_escape_string($_POST['login'])."'")or die ("<br>Invalid query: " . mysql_error()); 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 users_login='".$login."', users_password='".$password."'"); header("Location: login.php"); exit(); } } ?> <form method="POST" action=""> Логин <input type="text" name="login" id="reg_inp" /><br /> Пароль <input type="password" name="password" id="reg_inp" /><br /> <input name="submit" type="submit" value="Зарегистрироваться"> </form> <?php if (isset($err)) { print "<b>При регистрации произошли следующие ошибки:</b><br>"; foreach($err AS $error) { print $error."<br>"; } } ?> |
login.php
Опять кратко расскажу о действиях совершаемых в данном скрипте. В самом начале у нас висит функция для генерации случайной строки, она служит для хеша пользователя (чуть позже более подробно). Далее мы проверяем наличие куков с ошибками (они ставятся в check.php). Подключаем файл конфигурации и проверяем пользователя. Вытаскиваем из бд логин и пароль, сравниваем с введенными и генерируем хеш. Записываем в бд новый хеш пользователя и ставим куки. В куках находится id и хеш пользователя. Пересылаем пользователя на check.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | <?php # Функция для генерации случайной строки function generateCode($length=6) { $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPRQSTUVWXYZ0123456789"; $code = ""; $clen = strlen($chars) - 1; while (strlen($code) < $length) { $code .= $chars[mt_rand(0,$clen)]; } return $code; } # Если есть куки с ошибкой то выводим их в переменную и удаляем куки if (isset($_COOKIE['errors'])){ $errors = $_COOKIE['errors']; setcookie('errors', '', time() - 60*24*30*12, '/'); } # Подключаем конфиг include 'conf.php'; if(isset($_POST['submit'])) { # Вытаскиваем из БД запись, у которой логин равняеться введенному $data = mysql_fetch_assoc(mysql_query("SELECT users_id, users_password FROM `users` WHERE `users_login`='".mysql_real_escape_string($_POST['login'])."' LIMIT 1")); # Соавниваем пароли if($data['users_password'] === md5(md5($_POST['password']))) { # Генерируем случайное число и шифруем его $hash = md5(generateCode(10)); # Записываем в БД новый хеш авторизации и IP mysql_query("UPDATE users SET users_hash='".$hash."' WHERE users_id='".$data['users_id']."'") or die("MySQL Error: " . mysql_error()); # Ставим куки setcookie("id", $data['users_id'], time()+60*60*24*30); setcookie("hash", $hash, time()+60*60*24*30); # Переадресовываем браузер на страницу проверки нашего скрипта header("Location: check.php"); exit(); } else { print "Вы ввели неправильный логин/пароль<br>"; } } ?> <form method="POST"> Логин <input name="login" type="text"><br> Пароль <input name="password" type="password"><br> <input name="submit" type="submit" value="Войти"> </form> <?php # Проверяем наличие в куках номера ошибки if (isset($errors)) {print '<h4>'.$error[$errors].'</h4>';} ?> |
check.php
И последний файл, который содержит проверку авторизации пользователя. В начале подключаем конфиг и если существуют куки начинаем проверку, если их нет, то ставим куки с номером ошибки и отсылаем на login.php. И так проверка. Вытаскиваем из бд id и хеш. Если они не проходят проверку на соответствие с теми куками которые стоят у посетителя, то удаляем существующие куки посетителя и ставим куки с номером ошибки, пересылаем на login.php. Если же все нормально то пользователь увидит страницу.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <?php # подключаем конфиг include 'conf.php'; # проверка авторизации if (isset($_COOKIE['id']) and isset($_COOKIE['hash'])) { $userdata = mysql_fetch_assoc(mysql_query("SELECT * FROM users WHERE users_id = '".intval($_COOKIE['id'])."' LIMIT 1")); if(($userdata['users_hash'] !== $_COOKIE['hash']) or ($userdata['users_id'] !== $_COOKIE['id'])) { setcookie('id', '', time() - 60*24*30*12, '/'); setcookie('hash', '', time() - 60*24*30*12, '/'); setcookie('errors', '1', time() + 60*24*30*12, '/'); header('Location: login.php'); exit(); } } else { setcookie('errors', '2', time() + 60*24*30*12, '/'); header('Location: login.php'); exit(); } ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /> <title></title> </head> <body> hello! </body> </html> |
Выход
Выход можно осуществить любым удобным для вас способом просто удалив куки, допустим так:
1 2 3 4 5 6 7 8 9 | <form action="" method="post"><input type='submit' name='exit' value='Выйти'/></form> <?php if($_REQUEST['exit']) { setcookie('id', '', time() - 60*60*24*30, '/'); setcookie('hash', '', time() - 60*60*24*30, '/'); header('Location: login.php'); exit(); } ?> |
Сразу хочу предупредить о том что ваши файлы должны быть в кодировке UTF8 и БЕЗ BOM! Приятного пользования!
Так же, при переходе со страницы на страницу, происходит «разлогинивание». Не подскажете что делать, пожалуйста?
[Ответить]
С разлогиниванием, вроде, разобрался, но с организацией выхода всё ещё есть проблемы…
[Ответить]
ZekMan Reply:
Ноябрь 19th, 2013 at 05:48
Код для выхода можно вставить на любую страницу, можно вместо формы использовать $_GET и сделать ссылку вида
[Ответить]
Странно, при нажатии в форме на выход переход на login.php не происходит, просто обновляется страница check.php без каких-либо изменений
[Ответить]
куки не удаляются почему-то… не подскажете что делать?
[Ответить]
ZekMan Reply:
Ноябрь 19th, 2013 at 18:19
куку нужно выставить с отрицательным временем «создания».
Попробуйте сразу после «удаления» кук сделать
2
die;
Таким образом мы будем знать удалились ли куки, если они действительно удалились то нужно смотреть почему они были созданы снова.
[Ответить]
есть дело к Вам. Нужна помощь. Договоримся. Вот мой e-mail: nazar-00@list.ru. Спасибо.
[Ответить]
Увы, ошибка при попытке выхода: array(3) { [«id»]=> string(1) «5» [«hash»]=> string(32) «ddab685ce0f59420974e007eb79d8afd» [«_ym_visorc»]=> string(1) «w» }
[Ответить]
т.е. куки не удаляются… смысл каков: заходим на index.php, авторизуемся в форме логин-пароль, происходит автопереход на index_in_action.php, там проверка на авторизацию стоит, если авторизованный юзер, то выводим в меню авторизации вместо формы логин-пароль форму выхода. по нажатию на выход куки должны удаляться и должен происходить переход обратно на index.php, но переход не происходит, куки не удаляются. Может так вам будет легче определить проблему? вот код для выхода:
if($_REQUEST[‘exit’])
{
if (isset($_COOKIE[‘id’]) and isset($_COOKIE[‘hash’]))
{
echo «something»;
setcookie(‘id’, », time() — 3600, ‘/’);
setcookie(‘hash’, », time() — 3600, ‘/’);
var_dump($_COOKIE);
die;
header(‘Location: index.php’); exit();
echo «some»;
}
}
[Ответить]
пишется «something», куки не перезаписываются, печатается var_dump, всё, переход не происходит, ну, либо каким-то магическим образом перебрасывает на index.php, где происходит проверка на наличие куков и, соответственно, сама авторизация, если куки не удалены, то происходит обратный переброс на index_in_action.php. Похоже, что куки действительно не удаляются. Но почему???
[Ответить]
ZekMan Reply:
Ноябрь 21st, 2013 at 04:35
Странно, сделайте копию вашего кода и отправьте мне на почту. или же загрузите на govnokod.com
[Ответить]
скажи, а почему вот, когда я сохраняю файлы в винде-1251, и , потом повторно открываю их, то у мени выдаются иероглифы вместо букв в программе и в браузере) почему?(
[Ответить]
ZekMan Reply:
Ноябрь 23rd, 2013 at 16:31
Вообще тут все зависит от вашей среды программирования и того как вы сохраняете файлы. А вообще я рекомендую UTF8 использовать повсеместно. Это можно сказать стандарт
[Ответить]
Собрал данную авторизацию на свой самописный проект. в итоке получаю следующую ошибку:
Warning: Cannot modify header information — headers already sent by (output started at Z:\home\localhost\www\1\includes\login.php:1) in Z:\home\localhost\www\1\includes\login.php on line 33
Warning: Cannot modify header information — headers already sent by (output started at Z:\home\localhost\www\1\includes\login.php:1) in Z:\home\localhost\www\1\includes\login.php on line 34
Warning: Cannot modify header information — headers already sent by (output started at Z:\home\localhost\www\1\includes\login.php:1) in Z:\home\localhost\www\1\includes\login.php on line 37
Это касается вот этих строк:
setcookie(«id», $data[‘id’], time()+60*60*24*30);
setcookie(«hash», $hash, time()+60*60*24*30);
header(«Location: check.php»);
[Ответить]
Проблема была BOM
[Ответить]
ZekMan Reply:
Декабрь 27th, 2013 at 10:18
Я рад что Вы разобрались, все таки кто то читает мои напоминания про BOM
[Ответить]
Скажите, я правильно понимаю, что если куки украдут, то зайти в аккаунт можно без проблем?
Как от этого защититься ?
[Ответить]
ZekMan Reply:
Январь 11th, 2014 at 07:32
Добрый день, да этот вариант использует только куки, обратите внимание на http://programmer-weekdays.ru/archives/395 там я использовал связку кук и сессий
[Ответить]
А зачем хранить логины в закодированном виде? Как пароли восстанавливать пользователям?
[Ответить]
упс, невнимательность, не на то поле глянул в MySql ;-[
[Ответить]
ZekMan Reply:
Январь 11th, 2014 at 15:01
Вот всегда так, сначала спросят — потом посмотрят =\
[Ответить]
Привет! Спасибо за урок! Скажи пожалуйста, а как напечатать например user_name?
[Ответить]
ZekMan Reply:
Февраль 4th, 2014 at 15:24
Вам нужно добавить его в куки, смотрите login.php на 26 строке и далее
[Ответить]
Спасибо, статья что надо ) Новые, для меня веенья в авторизации ) усли совместить с ajax вообще конфетка получится )
[Ответить]
ZekMan Reply:
Февраль 9th, 2014 at 15:12
Спасибо
[Ответить]
Пытаюсь найти скрипт регистрации и авторизации для строго отдельной станицы. Поясню подробнее.
Посетитель регистрируется на главной странице с указанием определенного параметра, а при авторизации попадает на строго определенную страницу, автором которой он является с возможностью онлайн редактирования, при этом все остальные страницы закрыты для редактирования.
Авторизация на куках не подходит. Люди мобильные, входят на сайт с разных гаджетов из разных точек доступа к сети. Им что системник с собой таскать, в котором куки? Может кто знает, где взять скрипт авторизации, ну например, по файл-ключу на флешке или по дополнительному кодовому слову, по названию страницы сайта наконец. Сколько не искал — у всех на допотопных и не надежных куках. Каменный век какой-то.ам я не программист, скрипты не пишу
[Ответить]
ZekMan Reply:
Февраль 12th, 2014 at 06:06
У меня тут же в блоге лежит авторизация на сессиях + куках. Объясню, сессия — есть текущая активная сессия (30 минут после закрытия вкладки она так же будет активна, т.е. человек повторно зайдя на сайт в течении этих 30 минут будет авторизован), что бы увеличить срок в течении которого пользователь авторизован используются куки. При связке кук и сессий мы можем существенно изменить время которое пользователь авторизован.
Чем вас не устраивает авторизация по паре логин/пароль? Авторизация по файл ключу — на мобильных гаджетах это геморрой, как для пользователя так и для программиста. Простейшую авторизацию можно написать и по связке телефонный номер/уникальный код ( по сути тот же логин пароль) или просто уникальный код.
Напишите мне на почту — пообщаемся, может смогу помочь.
[Ответить]
Всем привет )
Народ подскажите вот такой вопрос,я создал страничку на неком сайте,лежит она в корне сайта,как мне сделать так что бы человек зашедшей на эту страницу без авторизации на самом сайте,не смог воспользоваться контентом пока не пройдёт авторизацию.
Кто знает отпишите в личку на мыло.
Спасибо.
[Ответить]
ZekMan Reply:
Февраль 12th, 2014 at 06:00
Посмотрите на файл check.php
[Ответить]