ФорумПрограммированиеПыхнуть хотите?F.A.Q. → Авторизация пользователей в полном смысле этого слова.

Авторизация пользователей в полном смысле этого слова.

  • md5

    Сообщения: 11960 Репутация: N Группа: в ухо

    Spritz Дек. 9, 2009, 9:47 д.п., спустя 5 дней 23 часа 20 минут

    Авторизация пользователей в полном смысле этого слова.


    1. Таблица Базы данных с пользователями

    Мы будем использовать очень краткий вариант. Структура будет такова:
    id smallint(8) unsigned NOT NULL auto_increment,
    login varchar(50) NOT NULL default '',
    password varchar(32) NOT NULL default ''

    Размер поля password — 32 символа. Т.к. мы бедм хранить в нем хеш пароля, а не сам пароль в чистом виде.


    2. Авторизация и вход пользователей собственно

    Для начала мы отрисуем форму, с помомью которой пользователь сможет передать нам свой логин и пароль.

    <form action="login.php" method="post">
    <table>
    <tr>
    <td>Логин:</td>
    <td><input type="text" name="login" /></td>
    </tr>
    <tr>
    <td>Пароль:</td>
    <td><input type="password" name="password" /></td>
    </tr>
    <tr>
    <td></td>
    <td><input type="submit" value="Войти" /></td>
    </tr>
    </table>
    </form>


    После ввода пользователем данных, мы проверяем его логин и пароль.

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


    if (isset($_POST['login']) && isset($_POST['password']))
    {
    $login = mysql_real_escape_string($_POST['login']);
    $password = md5($_POST['password']);

    // делаем запрос к БД
    // и ищем юзера с таким логином и паролем

    $query = "SELECT `id`
    FROM `users`
    WHERE `login`='{$login}' AND `password`='{$password}'
    LIMIT 1";
    $sql = mysql_query($query) or die(mysql_error());

    // если такой пользователь нашелся
    if (mysql_num_rows($sql) == 1)
    {
    // то мы ставим об этом метку в сессии (допустим мы будем ставить ID пользователя)

    $row = mysql_fetch_assoc($sql);
    $_SESSION['user_id'] = $row['id'];

    // не забываем, что для работы с сессионными данными, у нас в каждом скрипте должно присутствовать session_start();
    }
    else
    {
    die('Такой логин с паролем не найдены в базе данных. И даём ссылку на повторную авторизацию.');
    }
    }



    3. Кто авторизован, а кто нет?

    Итак мы определились, что авторизованные пользователи — те, у которых указан $_SESSION['user_id']
    Те, у кого нет $_SESSION['user_id'], мы будем считать гостями.

    Пример использования:

    if (isset($_SESSION['user_id']))
    {
    // показываем защищенные от гостей данные.
    }
    else
    {
    die('Доступ закрыт, даём ссылку на авторизацию.');
    }



    4. Как реализовать "запомнить меня на этом компьютере"?

    Здесь, помимо сессий, мы будем использовать куки.
    О куках обязательно нужно почитать у нас в FAQ.

    Сессии не постоянны и данные в них хранятся только на время работы пользователя со скриптом. Поэтому, чтобы пользователь оставался залогиненым после того, как он закрыл и снова открыл на нашей странице свой браузер — необходимо сохранять его авторизационные данные в куках (они хранятся на его компьютере в браузере).
    Мы будем записывать в куки пользователя его логин и хеш пароля. (как записывать что-то в куки, читайте выше по ссылке)

    В нашем скрипте  проверка пользовательских данных в куках будет выглядеть так:


    // если пользователь не авторизован
    if (!isset($_SESSION['user_id']))
    {
    // то проверяем его куки
    // вдруг там есть логин и пароль к нашему скрипту

    if (isset($_COOKIE['login']) && isset($_COOKIE['password']))
    {
    // если же такие имеются
    // то пробуем авторизовать пользователя по этим логину и паролю
    $login = mysql_real_escape_string($_COOKIE['login']);
    $password = mysql_real_escape_string($_COOKIE['password']);

    // и по аналогии с авторизацией через форму:

    // делаем запрос к БД
    // и ищем юзера с таким логином и паролем

    $query = "SELECT `id`
    FROM `users`
    WHERE `login`='{$login}' AND `password`='{$password}'
    LIMIT 1";
    $sql = mysql_query($query) or die(mysql_error());

    // если такой пользователь нашелся
    if (mysql_num_rows($sql) == 1)
    {
    // то мы ставим об этом метку в сессии (допустим мы будем ставить ID пользователя)

    $row = mysql_fetch_assoc($sql);
    $_SESSION['user_id'] = $row['id'];

    // не забываем, что для работы с сессионными данными, у нас в каждом скрипте должно присутствовать session_start();
    }
    else
    {
    // только мы не будем давай ссылку на форму авторизации
    // вдруг человек и не хочет был авторизованым
    // а пришел просто поглядеть на наши страницы как гость
    }
    }
    }



    4.1. Как установить куки?
    об этом написано опять же в нашем ФАКе
    Когда ставить? — когда авторизуем пользователя через форму, тогда и ставим куку, если в этом есть необходимость.


    5. О безопасности кук

    Ложа руку на сердце и *** на все холивары по этому поводу, ответственно заявляю: если вы ну оооочень боитесь утечки данных или они очень конфиденциальные — не сохраняйте ничего у пользователя (в куках). Куки могут быть легко угнаны (их даже перекрашивать не нужно) и выданы за свои.

    Просто предупреждайте пользователя, что если он хочет "оставаться авторизованым на Вашей странице", то всегда сохранятся риск потери данных.


    6. Безопасное хранение пароля в базе данных и т.д.


    Пароль не следует хранить в открытом виде. Всегда существует опасность official&client=firefox-a">sql injection, при которой злоумышленник может наглым образом получить Ваш пароль.
    Лучше его хешировать (например с помощью функции md5()).
    Прочитайте в мануале про эту функцию. И именно по этому мы отводим на пароль 32 символа.
    И следовательно, при авторизации мы сверяем не пароли, а их хеши. В нашем случае это было так: мы сравнивали md5('введенного пароля') с хэшем пароля, хранящимся в БД.


    7. Простое движение рук

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

    Поэтому, простым движением руки, мы можем немного сложнить задачу людям, которые соберутся подбирать пароль по его хешу.
    Можно брать двойной хеш (например, md5(sha1('password'))) или использовать так называемую "соль" (salt).
    Пример использования соли: md5(md5('password') . 'secret_code');
    secret_code — это и есть соль, то есть мы тупо к паролю добавляем какой-то набор символов (желательно ещё и запомнить, какой набор символов мы добавляем ;)).

    Итак, для такого метода хранения пароля мы немного изменим таблицу БД с пользователями:

    id smallint(8) unsigned NOT NULL auto_increment,
    login varchar(50) NOT NULL default '',
    password varchar(32) NOT NULL default '',
    salt varchar(3) NOT NULL default ''

    Здесь мы будем использовать соль из 3 символов.

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

    Рассмотрим пример:
    — мы регистрируем пользователя: md5 с паролем password и солью 8f*
    — получаем его хеш, используя md5(md5('password') . '8f*')
    — записываем пользователя в таблицу, получаем:
    1 | md5 | 84cd3e7ff13bbaed1c1db91671844bcc | 8f*
    — при входе пользователя через фому, немного изменим наш код:

    // дня начала вытащим из таблицы с пользователями соль для логина, который был введен
    $login = mysql_real_escape_string($_POST['login']);

    $query = "SELECT `salt`
    FROM `users`
    WHERE `login`='{$login}'
    LIMIT 1";
    $sql = mysql_query($query) or die(mysql_error());

    if (mysql_num_rows($sql) == 1)
    {
    $row = mysql_fetch_assoc($sql);

    // итак, вот она соль, соответствующая этому логину:
    $salt = $row['salt'];

    // теперь хешируем введенный пароль как надо и повторям шаги, которые были описаны выше:
    $password = md5(md5($_POST['password']) . $salt);

    // и пошло поехало…
    }
    else
    {
    die('пользователь с таким логином не найден, даём ссылку на повторную авторизацию');
    }



    8. Полезные ссылки
    Сессии
    Cookie
    md5()
    sha1()


    9. Наглядно
    Во вложении к сообщению имеется архив, включающий в себя скрипты: авторизации, регистрации; пример закрытой страницы и дамп таблицы пользователей.
    здесь реализовано хранение пароля в виде: md5(md5('пароль') . 'соль') и запоминание с помощью кук

    * по вопросам регистрации, хочу добавить, что в примере отображена простая регистрация, не требующая подтверждения по ссылке из письма.. возможно позже выложу, если у кого есть желание
    все умрут, а я изумруд
  • pasha

    Сообщения: 1048 Репутация: N Группа: Адекваты

    Spritz Авг. 18, 2007, 4:32 д.п., спустя 17 часов 45 минут 9 секунд

    // не забываем, что для работы с сессионными данными, у нас в каждом скрипте должно присутствовать session_start();


    Во я помню мудил 2-а часа…че думаю не работает нихрена =)…жесть)
  • nwuno

    Сообщения: 26 Репутация: N Группа: Кто попало

    Spritz Ноя. 1, 2007, 10:08 д.п., спустя 75 дней 6 часов 35 минут

    хотел спросить что такое set character_set_client
    set character_set_results
    set collation_connection
    test_auth

    <?php

    mysql_connect("localhost", "root", "") or die (mysql_error());
    mysql_select_db("test_auth") or die (mysql_error());

    mysql_query("set character_set_client ='cp1251'");
    mysql_query("set character_set_results ='cp1251'");
    mysql_query("set collation_connection ='cp1251_general_ci'");


    ?>
  • md5

    Сообщения: 11960 Репутация: N Группа: в ухо

    Spritz Ноя. 1, 2007, 10:28 д.п., спустя 20 минут 8 секунд

    тупо переводим с инглиша:
    character_set_client — кодировка клиента
    character_set_results — кодировка результата
    collation_connection — сравнение

    устанавливаются кодировки

    или юзать SET NAMES CP1251
    (это всё для кодировки windows-1251)
    все умрут, а я изумруд
  • Aarstad

    Сообщения: 8 Репутация: N Группа: Кто попало

    Spritz Ноя. 4, 2007, 1:14 д.п., спустя 2 дня 14 часов 45 минут

    Можно брать двойной хеш (например, md5(sha1('password'))) или использовать так называемую "соль" (salt).
    Пример использования соли: md5('password' . 'secret_code');
    secret_code — это и есть соль, то есть мы тупо к паролю добавляем какой-то набор символов (желательно ещё и запомнить, какой набор символов мы добавляем ;))


    Насколько я понимаю, это бессмысленно. Ведь какую проблему мы пытаемся решить? Хакер взломав базу получает md5 хэш и при помощи радужных таблиц находит строку из которой этот хэш получен. Найдя эту строку он отнимет от нее соль (которую также найдет во взломанной базе) и получит пароль.

    Соль надо добавлять к уже сгенерированному хэшу. В этом случае мы получим просто набор символов, расшифровать который можно только зная длину соли и имея таблицы для всех вариантов соли. А также зная куда эту соль добавили.
    Также, получается, что соль в базе хранить нельзя (иначе можно отнять ее от хеша и дальше работать по таблицам с ним). Лучше задать ее однажды в скрипте и добавлять ко всем хэшам.

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

    Кроме того, можно добавлять к уже готовому хэшу пароля в качестве соли использовать какое-нить неочевидное поле (например хэш даты регистрации пользователя). Чтобы хакеру было сложнее определить что добавлено и куда.
  • AlexB

    Сообщения: 4306 Репутация: N Группа: в ухо

    Spritz Ноя. 4, 2007, 1:24 д.п., спустя 10 минут 37 секунд


    Хакер взломав базу получает md5 хэш и при помощи радужных таблиц находит строку из которой этот хэш получен.

    Ты сам то пробовал так сделать или только в инете прочитал?


    Найдя эту строку он отнимет от нее соль (которую также найдет во взломанной базе) и получит пароль.

    А откуда вообще возникла мысль, что кто-то станет хранить ее в базе?
  • Aarstad

    Сообщения: 8 Репутация: N Группа: Кто попало

    Spritz Ноя. 4, 2007, 1:28 д.п., спустя 3 минуты 20 секунд



    Хакер взломав базу получает md5 хэш и при помощи радужных таблиц находит строку из которой этот хэш получен.

    Ты сам то пробовал так сделать или только в инете прочитал?


    Это сложно, но возможно в теории…


    Найдя эту строку он отнимет от нее соль (которую также найдет во взломанной базе) и получит пароль.

    А откуда вообще возникла мысль, что кто-то станет хранить ее в базе?


    Ну я рассматриваю скрипт предложенный автором топика — он хранит полученный хэш и соль в базе.
  • AlexB

    Сообщения: 4306 Репутация: N Группа: в ухо

    Spritz Ноя. 4, 2007, 1:32 д.п., спустя 4 минуты 45 секунд


    Ну я рассматриваю скрипт предложенный автором топика — он хранит полученный хэш и соль в базе.


    А да, действительно … я бы ее там не хранил.
    Но суть не в этом, а в том что написано здесь http://pyha.ru/forum/topic/68.0
  • Aarstad

    Сообщения: 8 Репутация: N Группа: Кто попало

    Spritz Ноя. 4, 2007, 1:37 д.п., спустя 5 минут 9 секунд

    Это я читал. Согласен — узнать начальную строку будет очень проблематично. Особенно если ломают какую-то ерунду…
  • md5

    Сообщения: 11960 Репутация: N Группа: в ухо

    Spritz Ноя. 4, 2007, 4 п.п., спустя 14 часов 22 минуты 25 секунд

    Соль надо добавлять к уже сгенерированному хэшу.

    согласен, поправлено

    + в пример выложил простейший скрипт регистрации
    все умрут, а я изумруд
  • Aarstad

    Сообщения: 8 Репутация: N Группа: Кто попало

    Spritz Ноя. 5, 2007, 11:58 д.п., спустя 19 часов 57 минут 46 секунд


    Соль надо добавлять к уже сгенерированному хэшу.

    согласен, поправлено

    Да и соль и в базе, как уже указывалось, лучше тоже не хранить.

    Есть очень одна проблема в вашем скрипте — если сохраняться в кукисах, то из защищенной зоны невозможно будет выйти.))) После логаута нас переправляет на корневой раздел, который тут же логинится через кукисы. Чтобы решить эту проблему, можно ставить еще одну куку "logout" и проверять — если такая кука установлена, то показывать форму логина с уже заполненными полями, чтобы пользователь только нажал клавишу если решит войти; если куки "logout" нет, то логиниться сразу. Можно конечно при логауте просто удалять кукисы, но на мой взгляд менее удобно для конечного пользователя.
  • md5

    Сообщения: 11960 Репутация: N Группа: в ухо

    Spritz Ноя. 5, 2007, 12:14 п.п., спустя 16 минут 4 секунды

    Есть очень одна проблема в вашем скрипте — если сохраняться в кукисах, то из защищенной зоны невозможно будет выйти.))) После логаута нас переправляет на корневой раздел, который тут же логинится через кукисы. Чтобы решить эту проблему, можно ставить еще одну куку "logout" и проверять — если такая кука установлена, то показывать форму логина с уже заполненными полями, чтобы пользователь только нажал клавишу если решит войти; если куки "logout" нет, то логиниться сразу. Можно конечно при логауте просто удалять кукисы, но на мой взгляд менее удобно для конечного пользователя.

    маразм

    зачем заполнять форму? какой смысл?
    человек обычно жмёт выход для того, чтобы не только сеанс закончить, а ещё и куки удалить, чтобы никто за его компом больше по закрытой зоне не лазил

    Вы же предлагаете, вобще не удалять ни логина ни пароля, а всегда их хранить в куках, пока человек не полезет в браузерные сетинги и вручную не удалит

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

    то, о чем говориты Вы — называется мастер хранения паролей к примеру и является фичей браузера


    *p.s.

    согласен с тем, что если люди просят им регистрацию, то и "выход" — тоже не сделают =)
    все умрут, а я изумруд
  • Aarstad

    Сообщения: 8 Репутация: N Группа: Кто попало

    Spritz Ноя. 5, 2007, 12:22 п.п., спустя 7 минут 58 секунд

    Нет нет… девушка лет шестнадцати, должна знать, что лучше не сохраняться в кукисах в клубах. А если она не в курсе, то и кнопку выход она жать не будет. [Можно подпись к чекбоксу сделать такую, чтобы ясно было, как сохраняется пароль — "запомнить меня на этом компьютере"].
    Человек же выходит из приватной зоны не обязательно для того, чтобы никто другой не смог войти. Он, к примеру, желает войти под другим именем.

    Но вы правы, если просто сделать так, как я писал в предыдущем посте, то кукисы можно будет удалить только руками. Но это легко исправить, надо просто удалять установленные кукисы, если человек входит со снятой галкой "запомнить меня". На мой взгляд, это весьма очевидно, и кроме того довольно распространено в природе. Например на mail.ru. Там правда сделано по-уродски все: по-умолчанию тебя запомнят, если не поставить галку. Надо делать конечно наоборот — по-умолчанию не запоминать.

    Не ну я как бы не настаиваю, ваш скрипт — ваш выбор. Но поправьте его, чтобы выйти хотя бы можно было в принципе.
  • md5

    Сообщения: 11960 Репутация: N Группа: в ухо

    Spritz Ноя. 5, 2007, 12:50 п.п., спустя 28 минут 21 секунду

    Но поправьте его, чтобы выйти хотя бы можно было в принципе.

    да, это добавлю

    + добавлю "галочку" запоминать или нет

    здесь показан механизм, как в принципе реализуется запоминание, а не готовый скрипт, который дёрнуть, перекрасить и к себе в супер-гостевую
    хочется, чтобы люди понимали
    все умрут, а я изумруд
  • Aarstad

    Сообщения: 8 Репутация: N Группа: Кто попало

    Spritz Ноя. 5, 2007, 12:57 п.п., спустя 6 минут 50 секунд

    + добавлю "галочку" запоминать или нет

    простите, не понял о чем вы…

    а насчет механизма — безусловно вы правы, и я вас полностью поддерживаю.

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

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