Портал Belkin-labs»PHP классы»Статья
welcome!

Некоторые идеи по безопасному входу (авторизации)

В чем, собственно, проблема?

Есть сайт. Сайт расположен на обычном публичном хостинге и доступ к нему осуществляется по небезопасным, нешифрованным каналам. По протоколу HTTP. Таких сайтов большинство. И неважно, как они реализованы, в виде магазина или в виде блога, или, как у меня, в виде сайта авторских статей, у каждого есть некая админка, предназначенная для работы администратора. Конечно, на многих сайтах есть и авторизация пользователей, но в этой статье мне хотелось бы сосредоточиться хотя бы на одном пользователе. На администраторе.

И вот вся проблема в том, что в процессе авторизации, при отсылке логина и пароля на сервер, эти сведения передаются совершенно открытым способом. Любая нюхалка трафика (сниффер) мгновенно покажет логин и пароль администратора. И это крайне прискорбно, поскольку злоумышленник воспользуется этими сведениями для того, чтобы положить на сайт какой-нибудь примитивный шелл, а где-нибудь через недельку, когда все логи доступа уже забудут об этом событии, шеллу будет дана некоторая команда и все файлы .htaccess в одно мгновение будут испорчены. В них будет подсажен вызов вируса, а сами файлы будут запрещены от записи для всех, включая их владельца. После этого какая-нибудь не в меру умная и проворная программка, типа антивируса Касперского, зафиксирует это событие, причем сделает это значительно раньше Гугла и занесет информацию о сайте в свою базу данных. После этого трафик на сайт обрушится, ибо Каспер просто запретит на него вход тысячам своих пользователей.

А что дальше? А дальше еще печальнее. Администратор ищет шелл. Ну, предположим, находит. Дальше он ищет вирусняк и обнаруживает порченные файлы .htaccess, каждый из которых защищен от записи. А файликов таких на приличном сайте может быть столько же, сколько и папок. А папок может быть и десять, и двадцать и больше. Но это только половина дела. После исправления файлов и удаления вредоносных вызовов, админ наконец-то подходит к главному. А что же делать с Каспером? А вот тут проблема, ибо админ, как продвинутый пользователь, работает на Линуксе и никаким Каспером не пользуется и у него даже идеи такой не появлялось, купить Каспера для Windows больше чем за полторы тысячи рублей, а потом еще и обновлять его по тыще тех же рублей в год. И куда писать? Какая же поддержка послушает пользователя, который мало того, что вирусы по интернету распространяет, но еще и не желает башлять за защиту от них? Да, дорогие друзья! Вот так рушатся карьеры администраторов сайтов интернет-магазинов. Веселая история?

Хотелось бы, чтобы эта история так и осталась историей и никогда не стала действительностью. Что же для этого можно и надо сделать?

Возможные варианты повышения безопасности авторизации админа на собственном сайте

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

Причем поговорить я хочу именно об авторизации, хотя эта проблема не стоит на первом месте в обеспечении безопасности сайта. Прошу меня извинить.

Давайте договоримся, что решать проблему путем покупки сертификата и перехода на HTTPS мы не будем, хотя это совсем неплохой вариант. Кстати, не стопроцентный. Что у нас остается из более доступных вариантов? Из раздела "Своими руками", так сказать?

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

Авторизация по одноразовым ссылкам

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

И все бы хорошо. Но на практике это все как-то напряжно происходит. А самое интересное, что почтовые сервисы не отличаются надежностью. С почтового сервиса родного хостинга я ушел уже много лет назад. Но и Яндекс с Гуглом тоже не отличаются надежностью и письма для входа, бывает нужно ждать и 15 минут, и час, и два, а один раз я сутки ждал. Согласитесь, с этим мириться нельзя. Нужна какая-то альтернатива. Причем старый добрый нешифрованный вход мы не рассматриваем. И все-таки, вход по одноразовым ссылкам - вариант вполне рабочий. Давайте рассмотрим его реализацию подробнее.

Для входа по одноразовой ссылке нужно реализовать некий механизм. В первую очередь необходима отдельная таблица в базе данных. В ней будем учитывать секретные ссылки. Сама секретная ссылка - просто достаточно длинный набор символов, выбранных в случайном порядке. Я считаю, что 50-ти хватит. Но можно сделать и больше. Кроме самой секретной ссылки хорошо бы учитывать время ее создания и длительность ее действия. Согласитесь, секретная ссылка не должна действовать вечно. У нее должен быть разумный срок действия. Кроме этих сведений я запоминаю к этой ссылке некоторые другие сведения. В любом случае я запоминаю ее назначение. То есть когда скрипт анализирует параметр с секретной строкой, он, во-первых, ищет ее в таблице секретных ссылок, а, во-вторых, сверяет нужное действие с тем, которое было записано этой секретной ссылке в момент ее создания.

Действует система секретных ссылок примерно так.

  1. Мы заходим на определенную страницу. Скрипт устанавливает текущее время, время жизни ссылки, операцию, которая будет производиться по ссылке и саму ссылку в виде строки случайных символов. Сформировали запрос и записали эти вещи в таблицу секретных ссылок. Напоследок послали письмо на известный почтовый адрес. В письме полностью сформированная ссылка для входа на сайт.
  2. мы щелкаем по ссылке. При этом мы сначала проверяем параметр с секретной ссылкой. Если в нем нет ничего опасного или неожиданного, например, пробелов, кавычек, знаков равенства и так далее, то первое, что мы делаем, это удаляем из таблицы секретных ссылок все просроченные ссылки. Потом мы ищем в таблице пришедшую строку. Если находим, проверяем операцию. Можно искать сразу и строку ссылки и операцию. Если все совпало, удаляем отработанную ссылку из таблицы и осуществляем авторизацию пользователя. Вот и все!

А что делать, если у нас куча администраторов? Тогда все немного сложнее, но не фатально. Тогда на странице входа нужно выбрать логин, который хочет войти по одноразовой ссылке. После этого на адрес пользователя отсылается письмо с секретной ссылкой. В таблицу секретных ссылок добавляем поле "Данные", в которые записываем логин пользователя. А дальше, кажется, все понятно. Нашли ссылку с операцией "авторизация пользователя", прочитали логин и авторизовали этого пользователя по логину.

Кажется мы этим простым механизмом решаем все проблемы незашифрованного входа. Если бы только почтовые сервера работали всегда так, как положено!

Авторизация по зашифрованным данным а-ля LastPass.

Ну хорошо. А вот хорошо было бы зайти на свой сайт не по одноразовой ссылке, а по зашифрованной. Это значительно сложнее в реализации, чем вход по секретной ссылке. Но тоже можно реализовать. Я реализовал и, кажется, учел все подводные камни. Смотрите, как я сделал. Вдруг я не все учел?

Сложности при проектировании входа по зашифрованным данным

  • Передавать шифрованные сведения в любом случае придется через GET или POST. Проще через GET. На это и будем рассчитывать.
  • Ссылки с шифром не должны повторяться. Если они будут всегда одинаковые, то достаточно спереть параметр и пользоваться. И неважно, будет он зашифрован или нет. Все, что мы получим в этом случае, это то, что злоумышленник не узнает нашего логина и пароля. Но все равно зайдет на сайт.
  • Должно быть невозможно зайти на сайт по отработавшей ссылке. Ссылки должны быть одноразовыми.
  • Очевидно, что мы не можем генерить ссылку для входа средствами сайта. Это значит у нас должна быть либо локальная страница, которая генерит зашифрованную ссылку, либо должен быть реализован аддон для Фокса или для того браузера, которым вы пользуетесь. Именно так и реализован LastPass
  • Сайт и страница, генерирующая ссылки, должны точно знать ключ, для того, чтобы было возможно расшифровать сведения для авторизации и проверить их.

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

  • Для генерации секретной ссылки я пользовался локальной страницей. Ее нет на сервере. В ней стоит соответствующая проверка.
  • Для шифрования сведений я пользовался модулем mcrypt.
  • В классе, который занимается у меня шифрованием и дешифрованием, я учредил свойство, которое хранит длинную, 500 символов или больше, строку случайных символов. Именно она является поставщиком так называемых ключа и вектора для модуля шифрования и дешифрования. Таким образом, нам нужно знать не сам ключ и вектор, а только 4 числа. Начальный индекс для взятия ключа и его длину. То же самое для вектора. Длину примем в 100 символов. Тогда вся наша секретность упрощается до знания всего двух чисел начальных индексов для отбора сотни символов из парольной строки. Очевидно, что если строка у нас 500 символов, то индексы эти находятся в диапазоне 0-399. Иначе мы сотню символов не сможем взять.
  • И вот путем хитрого манипулирования меткой времени Unix, я смог добиться того, что каждый день я получаю новый начальный индекс для ключа и новый начальный индекс для вектора. Таким образом, ключи шифрования меняются каждый день, что довольно часто для ключей. Манипуляции хоть и были хитрые, но совсем несложные. Каждый при некотором напряжении мозгов легко придумает такую функцию. Очевидно, что и локальная страница и сайт обучены использованию такой функции и получают из одинаковой парольной строки одинаковые ключи и вектора. Для этого нужно только чтобы на сервере и на локальной машине было одинаковое число. Даже не время.
  • Сами данные для авторизации, которые подвергаются шифрованию, включают не только логин и пароль. Они включают куда больше данных, некоторые из которых тоже рассчитываются на основании метки времени Юникс. Таким образом, я добился того, что зашифрованные строки не повторояются никогда вообще.
  • Осталась всего одна проблема. Надо добиться, чтобы ссылки на авторизацию были одноразовыми. Тут оказалось все просто. После расшифровки строки мы получаем метку времени и записываем ее в таблицу пользователей в запись того пользователя, который авторизуется. При авторизации нам надо соблюсти всего одно простое условие. Нам надо, чтобы метка времени из расшифрованных сведений для авторизации была больше чем та, которая записана в базе данных. Вот так мы отсекаем все ранее присланные сведения.

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

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

Напоследок только скажу, что зашифрованная строка содержит всякие неприятные символы, например с кодом 0 (ноль), и в общем случае является бинарной. Для формирования ссылки я кодирую полученную строку сначала функцией base64_encode(), а затем urlencode(). В итоге получается вполне пристойная ссылочка. Кроме того, я в некоторых местах использую прием двойного шифрования, что еще больше увеличивает секретность.

Итог по шифрованному входу

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

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

Вот и все! Спасибо за внимание.
Дмитрий Белкин

Статья создана 05.06.2013
Похожие материалы - отбираем по ключевым словам