SRP-6: аутентификация без передачи пароля

Как и было обещано в соседней теме, где рассказывался велосипед, выкладываю описание алгоритма SRP RFC2945 — способе регистрации и аутентификации пользователей безопасным образом по небезопасному каналу. Вот только в процессе подготовки статьи я обнаружил более свежую версию протокола, SRP-6, вместе с реализацией, в связи с чем решил выбросить свои архаичные наработки по SRP-3, и просто дать ссылки на имплементацию новой версии.

Secure Remote Password (SRP) протокол описывает установление безопасного соединения, используя для аутентификации пару логина и пароля, при которой пароль используется только на этапе регистрации и аутентификации и только на стороне клиента. Благодаря использованию схемы близкой к Диффи-Хеллману, SRP обеспечивает безопасную аутентификацию пользователя по паролю без необходимости передачи этого пароля, и даже имея полную запись обмена по каналу мы не получаем информации достаточной, для аутентификации данным пользователем. Так же, если сервер будет скомпрометирован и утечёт «в народ» вся база логинов со стороны сервера, злоумышленник не сможет аутентифицироваться ни одним из пользователей.x

Итак, общая схема аутентификации пользователя используя сокращенный по числу обменов SRP выглядит так:

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

Для реализации входа, можно использовать классическую форму логин+пароль, на onSubmit которой повешен аутентификатор.
В случае отсутствия на стороне клиента яваскрипта, форма отправится «как есть», что передаст по небезопасному каналу пароль, но позволит аутентифицироваться даже с «тупых» клиентов (путем симуляции всех действий «пользователя» на сервере).
В случае присутствия JS, аутентификация реализуется путем отправки двух спец-форм используя AJAX методики, производя вычисления между ними.

Впрочем, если сайт не требуется чтобы работал без JS (например, fallback вообще не возможен, слишком сложен, «не тянем» по времени и силам разработчиков), то мне кажется более разумным реализация такой схемы: поля вообще не находятся в форме, после выхода из поля логина поле пароля блокируется на некоторое время, и рисуется рядом ajax спиннер. После получения с сервера соли для юзернейма, поле пароля разблокируется и можно ввести туда пароль.
Смысл в разделении необходим для того, чтобы дать JS время на вычисления. Всё-таки там довольно много математики с большими числами. Когда я исследовал этот вопрос на SRP-3 используя SHA1 (лет пять эдак назад), использовать 512битные ключи оказалось слишком медленным — до 30 секунд на вычисления для подтверждения пароля. При этом весь UI браузера [особенно IE6 ;)] блокируется на всё время вычисления. Решением может быть вычисления используя континуации (запуск вычисления кусочками через setTimeout(), сохраняя контекст между вызовами) или вынося вычисления во flash / java. Причем java предпочтительнее с точки зрения скорости вычисления, а flash — с точки зрения вероятности доступности у пользователя.

Важный момент — сервер может отвечать солью на любой заданный ему username, чтобы не давать информации о существовании указанного пользователя у него в базе, но тогда соль для несуществующих пользователей должна вычислять по какому-либо алгоритму, на базе username, причем данный алгоритм должен быть сохранен в тайне, или быть неповторяемым без каких-либо секретных сведений на стороне сервера. Проще говоря, необходимо чтобы соль, базирующуюся на username было невозможно отличить от полностью рандомной соли реальных пользователей.

Еще очень важно понимать, что алгоритм никак не защищает от фишинга. Поддельный сайт вполне может выдать идентично выглядящую страницу, на которой будут так же расположенные поля логина и пароля, вплоть до симуляции вычислений вычисляя, например, сумму ряда чисел от 1 до 1e12 через однострочник, на самом деле передавая на сервер злоумышленника пароль «как есть». Именно поэтому мне кажется использование подписанного Java аутентификатора более привлекательным — при подделке надо будет симулировать еще и подпись на ява-апплете авторизаторе… Что, в общем-то, используя человеческую лень и невнимательность по-прежнему возможно.

Готовые реализации SRP на JS можно найти по ссылкам:

Почитать еще по теме можно и нужно:
@datacompboy
09.06.2011 23:36 UTC
Первоисточник