Авторизация (php + mysql) и запоминание пользователей для начинающих

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. Безопасное хранение пароля в базе данных

Пароль не следует хранить в открытом виде. Всегда существует опасность SQL инъекции, при которой злоумышленник может наглым образом получить Ваш пароль.
Лучше его хешировать (например с помощью функции 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(‘пароль’) . ‘соль’) и запоминание с помощью кук.
Имеется дамп таблицы БД, его загрузить и необходимо прописать настройки соединения с БД в файле — mysql.php

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

Share