Пишем свою авторизацию на PHP и MySQL

Сентябрь 21st, 2010 Рубрики: MySQL, PHP coubertin.cz www.colvillewoodworking.com

Ну что же, сегодня я вам расскажу о безопасной авторизации на 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! Приятного пользования!

Теги: , , , ,

79 комментариев к “Пишем свою авторизацию на PHP и MySQL”

  1. Макс
    Ноябрь 18th, 2013 at 16:17
    1

    Так же, при переходе со страницы на страницу, происходит «разлогинивание». Не подскажете что делать, пожалуйста?

    [Ответить]

  2. Макс
    Ноябрь 18th, 2013 at 16:35
    2

    С разлогиниванием, вроде, разобрался, но с организацией выхода всё ещё есть проблемы…

    [Ответить]

    ZekMan Reply:

    Код для выхода можно вставить на любую страницу, можно вместо формы использовать $_GET и сделать ссылку вида

    1
    <a href="page.php?exit">выход</a>

    [Ответить]

  3. Макс
    Ноябрь 19th, 2013 at 09:37
    3

    Странно, при нажатии в форме на выход переход на login.php не происходит, просто обновляется страница check.php без каких-либо изменений

    [Ответить]

  4. Макс
    Ноябрь 19th, 2013 at 16:26
    4

    куки не удаляются почему-то… не подскажете что делать?

    [Ответить]

    ZekMan Reply:

    куку нужно выставить с отрицательным временем «создания».
    Попробуйте сразу после «удаления» кук сделать

    1
    2
    var_dump($_COOKIE);
    die;

    Таким образом мы будем знать удалились ли куки, если они действительно удалились то нужно смотреть почему они были созданы снова.

    [Ответить]

  5. Arime
    Ноябрь 20th, 2013 at 01:52
    5

    есть дело к Вам. Нужна помощь. Договоримся. Вот мой e-mail: nazar-00@list.ru. Спасибо.

    [Ответить]

  6. Макс
    Ноябрь 20th, 2013 at 14:25
    6

    Увы, ошибка при попытке выхода: array(3) { [«id»]=> string(1) «5» [«hash»]=> string(32) «ddab685ce0f59420974e007eb79d8afd» [«_ym_visorc»]=> string(1) «w» }

    [Ответить]

  7. Макс
    Ноябрь 20th, 2013 at 15:10
    7

    т.е. куки не удаляются… смысл каков: заходим на 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»;
    }
    }

    [Ответить]

  8. Макс
    Ноябрь 20th, 2013 at 15:31
    8

    пишется «something», куки не перезаписываются, печатается var_dump, всё, переход не происходит, ну, либо каким-то магическим образом перебрасывает на index.php, где происходит проверка на наличие куков и, соответственно, сама авторизация, если куки не удалены, то происходит обратный переброс на index_in_action.php. Похоже, что куки действительно не удаляются. Но почему???

    [Ответить]

    ZekMan Reply:

    Странно, сделайте копию вашего кода и отправьте мне на почту. или же загрузите на govnokod.com

    [Ответить]

  9. Данил
    Ноябрь 22nd, 2013 at 20:55
    9

    скажи, а почему вот, когда я сохраняю файлы в винде-1251, и , потом повторно открываю их, то у мени выдаются иероглифы вместо букв в программе и в браузере) почему?(

    [Ответить]

    ZekMan Reply:

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

    [Ответить]

  10. To_wave
    Декабрь 27th, 2013 at 00:15
    10

    Собрал данную авторизацию на свой самописный проект. в итоке получаю следующую ошибку:

    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»);

    [Ответить]

  11. To_wave
    Декабрь 27th, 2013 at 00:22
    11

    Проблема была BOM

    [Ответить]

    ZekMan Reply:

    Я рад что Вы разобрались, все таки кто то читает мои напоминания про BOM

    [Ответить]

  12. Федор
    Январь 10th, 2014 at 16:35
    12

    Скажите, я правильно понимаю, что если куки украдут, то зайти в аккаунт можно без проблем?
    Как от этого защититься ?

    [Ответить]

    ZekMan Reply:

    Добрый день, да этот вариант использует только куки, обратите внимание на http://programmer-weekdays.ru/archives/395 там я использовал связку кук и сессий

    [Ответить]

  13. Евгений
    Январь 11th, 2014 at 13:12
    13

    А зачем хранить логины в закодированном виде? Как пароли восстанавливать пользователям?

    [Ответить]

  14. Евгений
    Январь 11th, 2014 at 13:16
    14

    упс, невнимательность, не на то поле глянул в MySql ;-[

    [Ответить]

    ZekMan Reply:

    Вот всегда так, сначала спросят — потом посмотрят =\

    [Ответить]

  15. Danil
    Январь 26th, 2014 at 13:04
    15

    Привет! Спасибо за урок! Скажи пожалуйста, а как напечатать например user_name?

    [Ответить]

    ZekMan Reply:

    Вам нужно добавить его в куки, смотрите login.php на 26 строке и далее

    [Ответить]

  16. Максим
    Февраль 7th, 2014 at 21:10
    16

    Спасибо, статья что надо ) Новые, для меня веенья в авторизации ) усли совместить с ajax вообще конфетка получится )

    [Ответить]

    ZekMan Reply:

    Спасибо

    [Ответить]

  17. Владимир
    Февраль 10th, 2014 at 13:38
    17

    Пытаюсь найти скрипт регистрации и авторизации для строго отдельной станицы. Поясню подробнее.
    Посетитель регистрируется на главной странице с указанием определенного параметра, а при авторизации попадает на строго определенную страницу, автором которой он является с возможностью онлайн редактирования, при этом все остальные страницы закрыты для редактирования.
    Авторизация на куках не подходит. Люди мобильные, входят на сайт с разных гаджетов из разных точек доступа к сети. Им что системник с собой таскать, в котором куки? Может кто знает, где взять скрипт авторизации, ну например, по файл-ключу на флешке или по дополнительному кодовому слову, по названию страницы сайта наконец. Сколько не искал — у всех на допотопных и не надежных куках. Каменный век какой-то.ам я не программист, скрипты не пишу

    [Ответить]

    ZekMan Reply:

    У меня тут же в блоге лежит авторизация на сессиях + куках. Объясню, сессия — есть текущая активная сессия (30 минут после закрытия вкладки она так же будет активна, т.е. человек повторно зайдя на сайт в течении этих 30 минут будет авторизован), что бы увеличить срок в течении которого пользователь авторизован используются куки. При связке кук и сессий мы можем существенно изменить время которое пользователь авторизован.

    Чем вас не устраивает авторизация по паре логин/пароль? Авторизация по файл ключу — на мобильных гаджетах это геморрой, как для пользователя так и для программиста. Простейшую авторизацию можно написать и по связке телефонный номер/уникальный код ( по сути тот же логин пароль) или просто уникальный код.

    Напишите мне на почту — пообщаемся, может смогу помочь.

    [Ответить]

  18. Олег
    Февраль 11th, 2014 at 19:42
    18

    Всем привет )
    Народ подскажите вот такой вопрос,я создал страничку на неком сайте,лежит она в корне сайта,как мне сделать так что бы человек зашедшей на эту страницу без авторизации на самом сайте,не смог воспользоваться контентом пока не пройдёт авторизацию.
    Кто знает отпишите в личку на мыло.
    Спасибо.

    [Ответить]

    ZekMan Reply:

    Посмотрите на файл check.php

    [Ответить]

Страницы комментариев

Написать комментарий