Ну что же, сегодня я вам расскажу о безопасной авторизации на PHP и Cookie. Ну о том что она абсолютна безопасна я не говорю, ибо взломать можно все, но для маленького сайта она вполне подходит. Так же я предвижу комментарии о том что сессии безопаснее. Не спорю куки уступают сессиям в безопасности но для реализации простенькой авторизации вполне подходят. Подробности ниже.
И так, в базе у нас будет 1 база из 4 полей: users_id, users_login, users_password и users_hash. SQL запрос:
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;
- conf.php — Файл конфигурации, в котором так же содержится подключение к бд;
- register.php — Регистрация нового пользователя;
- login.php — Авторизация пользователя;
- check.php — Скрипт проверки авторизации;
Давайте разберем каждый файл.
conf.php
В константах содержится данные SQL подключения, а именно хост, логин, пароль и имя вашей базы. Тут же мы подключаемся к базе, устанавливаем кодировку в которой будут передаваться данные в бд и выбираем бд с которой будем работать. Так же тут содержится массив ошибок которые могут возникнуть при авторизации.
<?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.
<?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.
<?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. Если же все нормально то пользователь увидит страницу.
<?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>
Выход
Выход можно осуществить любым удобным для вас способом просто удалив куки, допустим так:
<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! Приятного пользования!
Добавить комментарий