PHP: делаем быструю проверку идентификация пользователя
Существует много методов, как можно проверять идентификацию пользователя. Рассмотрим один из наиболее популярных методов:
Метод очень надежен. При изменении слепка пароля в куки, пользователь теряет идентификацию.
К недостаткам можно отнести низкую производительность метода. Для каждой проверки нам нужно держать открытым соединение к базе данных, а так же нужно делать выборку из таблицы. А таблицы могут быть даже очень большими.
А что если отказаться от базы данных или любого другого хранилища для проверки подлинности информации о пользователе…
Первым делом приходит идея:
Этот метод имеет очень хорошую скорость, но он является очень небезопасным. Можно украсть куки у админа. Можно угадать алгоритм…
Ну а если придумать такой безопасный алгоритм?
Я не так давно читал книгу Дэна Брауна «Цифровая крепость», где была фантастическая история про коды, которые нельзя взломать. Хотя так и есть, все ломается, но там была чудесная фраза «Код зашифрован так, что даже если машина его взломает, то она об этом не сможет догадаться».
Именно эту идею я и реализовал. Сейчас будет история, как я к этому пришел. А вот правильно расшифрованная строка: «r««$«$r(‡С‡э€‡«r» и «®ЅnГ¬Иgu}ЯZҐ№їя». Как видим на вид точно такая, как и неправильно расшифрованная. Что скажете? Если еще остался интерес, тогда читаем дальше о том, как это работает.
— Получить из нее хеш проверки md5() в raw формате вида «1ы)-ЈЕУ°Xфў.Nю/»
— Соединить две строчки и зашифровать в base64.
Идея была отброшена еще на этапе разработки, так как безопасность не сильно высока. Важные данные остаются почти открытыми. После раздумий была придумана улучшенная идея.
— Получить из нее хеш проверки 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». Ключ мы будем хранить на сервере. И так финальная идея.
— Получить из нее контрольный 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, и он не должен страдать от нашей системы защиты. Я предлагаю реализовать обработку результатов так:
— «1» — пользователь идентифицирован полностью
— «-1» — 50%/50% возможно у пользователя сменился IP или же у него украли куки. Тут два варианта: сделать аналогично «0» или же сделать иное решение.
Мне нравится, как работает идентификация на Amazon: Вы остаетесь под своим аккаунтом, а при попытке важных действий или при попытке просмотра конфиденциальной информации у вас попросит ввести пароль. Точно также можно поступить, когда IP адрес не совпадает. Тогда пользователь не будет полностью терять идентификацию, а злоумышленник не сможет с аккаунтом сделать ничего критичного.
Вывод: Используя алгоритм мы можем получить строку вида «gcreIi9fw/P8bulq139V3ZI/xwu1rrY0HiicksKJruqc», которую мы можем сохранить в куки или в сессию. Мы можем её очень быстро расшифровать и получить всю нужную нам информацию о пользователе. Нужно также добавить, что алгоритм очень легко модифицировать, таким образом, чтобы никто кроме вас не знал, как он работает.
$_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 комментариев