PHP: делаем быструю проверку идентификация пользователя

PHP
imageСуществует много методов, как можно проверять идентификацию пользователя. Рассмотрим один из наиболее популярных методов:

$_COOKIE + база данных

В $_COOKIE мы храним ID пользователя, а так же слепок пароля (например md5). При каждой генерации страницы мы по ID находим в базе данных слепок пароля и сверяем его с тем, что хранится в $_COOKIE.

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


А что если отказаться от базы данных или любого другого хранилища для проверки подлинности информации о пользователе…
Первым делом приходит идея:

$_COOKIE без базы данных

Мы можем сохранить ID, а также хеш например md5(ID) в куки. Для проверки пользователя нам достаточно просто взять md5(ID) и сравнить с хешем, который хранится в куки. Если они совпали — значит все ок. Если нет…

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

Ну а если придумать такой безопасный алгоритм?

gcreIi9fw/P8bulq139V3ZI/xwu1rrY0HiicksKJruqc

А что если мы сможем создать безопасную строку, где мы сможем сохранить следующую информацию: ID пользователя, Класс пользователя на сайте, IP адрес пользователя или любую другую информацию. Кажется невозможным? Пример такой строки немного выше. Там есть и ID, и класс, и даже IP. Многие скажут: «Да это же обычный base64_encode». Да он таки обычный, но не совсем. Попробуем расшифровать, получим строку вида «ЃКЮ"/_ГуьnйjЧUЭ’? Зµ®¶4 (њ’В‰®књ». Вы скажете, что просто изначальная строка была преобразована. И соответственно была расшифрована не правильно. И вы опять правы!

Я не так давно читал книгу Дэна Брауна «Цифровая крепость», где была фантастическая история про коды, которые нельзя взломать. Хотя так и есть, все ломается, но там была чудесная фраза «Код зашифрован так, что даже если машина его взломает, то она об этом не сможет догадаться».

Именно эту идею я и реализовал. Сейчас будет история, как я к этому пришел. А вот правильно расшифрованная строка: «r««$«$r(‡С‡э€‡«r» и «®ЅnГ¬Иgu}ЯZҐ№їя». Как видим на вид точно такая, как и неправильно расшифрованная. Что скажете? Если еще остался интерес, тогда читаем дальше о том, как это работает.

1 идея

— Взять информацию вида 911-1-93.2.46.19 (ID и класс пользователя, IP адресс)
— Получить из нее хеш проверки md5() в raw формате вида «1ы)-ЈЕУ°Xфў.Nю/»
— Соединить две строчки и зашифровать в base64.

Идея была отброшена еще на этапе разработки, так как безопасность не сильно высока. Важные данные остаются почти открытыми. После раздумий была придумана улучшенная идея.

2 идея

— Взять информацию вида 911-1-93.2.46.19
— Получить из нее хеш проверки md5() в raw формате и зашифровать его в base64
— Зашифровать информацию (911-1-93.2.46.19) в base64
— В двух строках убрать символ «=» в конце
— Развернуть строки задом на перед
— Так как строка с хешем будет иметь длину 22 символа, можно base64 от строки с информацией разрезать на 3 части и сделать финальную строку по такой формуле 1/3(base64(info))+base64(hash)+2/3(base64(info))

Теперь все на много безопасней. Алгоритм стало тяжелей угадать. Но остался нюанс. Если человек или машина, которые будут взламывать код угадают алгоритм они вместо непонятных символов («1ы)-ЈЕУ°Xфў.Nю/») получат нашу информацию на блюдечке (911-1-93.2.46.19). Немного подумал. И у меня родился финальный алгоритм. Создать ключ, который состоит из 12 символов с ASCII кодами от 1 до 255. И заменить цифры, дефисы и точки в нашей строке-информации на символы из ключа. Таким образом, мы получим вместо «911-1-93.2.46.19» строку вида «r««$«$r(‡С‡э€‡«r». Ключ мы будем хранить на сервере. И так финальная идея.

Финальная идея

— Взять информацию вида 911-1-93.2.46.19
— Получить из нее контрольный md5() в raw формате и зашифровать его в base64
— Зашифровать информацию (911-1-93.2.46.19) нашим секретным ключом, а затем в base64
— В двух строках убрать символ «=» в конце
— Развернуть строки задом на перед
— Так как строка с хешем будет иметь длину 22 символа, можно base64 от строки с информацией разрезать на 3 части и сделать финальную строку по такой формуле 1/3(base64(info))+base64(hash)+2/3(base64(info))

Теперь мы имеем строку, которую невозможно расшифровать, не зная алгоритма и ключа. Подобрать алгоритм шифрования очень тяжело, так как правильно расшифрованная строка на вид ни чем не отличается от не правильно расшифрованной строки. А если даже правильно подобрать алгоритм, то, не имея ключа, очень тяжело восстановить цифры в нашей информации. И последнее, чтобы сделать поддельную строку нам еще нужно знать алгоритм, по которому мы генерировали наш контрольный md5 хеш.

Реализация

И так. Мы получаем очень стойкий алгоритм шифрования нашей информации с несколькими уровнями защиты. Теперь реализация (скачать можно сдесь).

Код прокомментирован. Добавлю комментарий к функции декодирования. Она возвращает 0, если хеш не совпал. Если хеш совпал, то функция возвращает массив, который состоит из нашей информации и последнего элемента. Последний элемент может иметь значение «1», если IP адрес в хеше совпал с текущим; «-1» если IP адрес не совпал.

Вы можете спросить зачем? Если вернулось -1, то есть вероятность того, что у человека украли куки. Но у пользователя может быть динамический IP, и он не должен страдать от нашей системы защиты. Я предлагаю реализовать обработку результатов так:

Степень валидности идентификации пользователя

— «0» — пользователь не идентифицирован
— «1» — пользователь идентифицирован полностью
— «-1» — 50%/50% возможно у пользователя сменился IP или же у него украли куки. Тут два варианта: сделать аналогично «0» или же сделать иное решение.

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

Вывод: Используя алгоритм мы можем получить строку вида «gcreIi9fw/P8bulq139V3ZI/xwu1rrY0HiicksKJruqc», которую мы можем сохранить в куки или в сессию. Мы можем её очень быстро расшифровать и получить всю нужную нам информацию о пользователе. Нужно также добавить, что алгоритм очень легко модифицировать, таким образом, чтобы никто кроме вас не знал, как он работает.


0 комментариев

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.