Новый взгляд на старые вещи: web-аутентификация.

Читая обсуждения, которые происходят на различных форумах, наткнулся на одну тему, которая подтолкнула меня поразмыслить над протоколами web-авторизации.

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

Основными требованиями к новому протоколу были следующие:
• Использование такой системы должно быть не намного сложнее, чем при использовании стандартной.

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

• Сделать невозможным кражу сессии пользователя.

• Нагрузка на сервер должна быть настолько минимальной, насколько это возможно.

Что получилось в результате моих размышлений – читайте ниже.Введение

Читая обсуждения, которые происходят на различных форумах, наткнулся на одну тему, которая подтолкнула меня поразмыслить над протоколами web-авторизации.

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

Основными требованиями к новому протоколу были следующие:
• Использование такой системы должно быть не намного сложнее, чем при использовании стандартной.

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

• Сделать невозможным кражу сессии пользователя.

• Нагрузка на сервер должна быть настолько минимальной, насколько это возможно.

Что получилось в результате моих размышлений – читайте ниже.

Мат. часть

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

Строго говоря, этот процесс состоит из двух частей – идентификации и аутентификации.
Идентификация – это предъявление пользователем какого-то уникального, присущего только ему признака-идентификатора. Это может быть пароль, какая-то биометрическая информация, например отпечаток пальца, персональный электронный ключ или смарт-карта и т.д.
Аутентификация – это процедура, проверяющая, соответствует ли пользователь с предъявленным идентификатором тому, за кого он себя выдает.

Эти процедуры неразрывно связаны между собой, поскольку способ проверки определяет, каким образом и что пользователь должен предъявить системе, чтобы получить доступ.

Другими словами, идентификация пользователя — это получение от него ответа на вопрос: «Кто ты?» Скажем, Вася. А аутентификация — это требование: «А теперь докажи, что ты именно Вася» и последующая проверка доказательств. То есть проверка, действительно ли пользователь является тем, за кого он себя выдает.

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

Всю работу данного модуля можно условно разделить на два этапа.

Предварительный, на котором модуль формирует «эталонный образец», например, запрашивает пароль пользователя (это именно тогда пароль запрашивается дважды, чтобы исключить ошибку его ввода) — по нему пользователь будет опознаваться впоследствии. Пароль может и назначаться пользователю — так бывает, например, в различных системах доступа в Интернет. Обычно модуль аутентификации хранит эталонные образцы в таблице соответствий «пользователь — эталон».

И завершающий этап, когда пользователь проходит аутентификацию и у него запрашивается аутентификационная информация, которая сравнивается с эталоном. На основании этого сравнения он считается опознанным или нет.

На самом деле в реальных системах эталонный пароль может храниться в таблице в зашифрованном виде (например, в файле /etc/shadow в Linux-системах) или вместо пароля сохраняется его хэш. Это не позволит злоумышленнику, получившему доступ к хранилищу эталонов, ознакомиться с паролями всех пользователей системы.

В настоящее время для аутентификации используется информация трех видов.

Первый, назовем его «знание» — последовательность символов, которую пользователь должен знать для успешного прохождения аутентификации. Простейший пример — парольная аутентификация, для которой достаточно ввести в систему свой идентификатор (например, логин) и пароль.

Второй вид информации, «обладание» — уникальное содержимое или уникальные характеристики предмета. Простейший пример — ключ от любого замка. В случае же компьютерной аутентификации в этом качестве выступают любые внешние носители информации: смарт-карты, электронные таблетки iButton, USB-токены и т. д.

И, наконец, третий вид аутентификации, «характеристика» — по биометрической информации, которая неотъемлема от пользователя. Это может быть отпечаток пальца, рисунок радужной оболочки глаза, форма лица, параметры голоса и т. д.

Часто применяется многоуровневая аутентификация, в которой информация этих типов комбинируются. Типичный пример: аутентификационная информация хранится на смарт-карте, для доступа к которой нужно ввести пароль (PIN-код). Такая аутентификация называется двухфакторной. Существуют реальные системы и с трехфакторной аутентификацией.

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

В нашем случае мы рассматриваем системы аутентификации на web-сайтах, поэтому второй и третий варианты нам не подойдут в виду своей специфики. Хотя было время, когда на аукционе EBay для аутентификации использовались брелоки, генерирующие временные ключи доступа, но это скорее исключение из правил.

Размышления

Итак, наша цель – системы парольной аутентификации (bypass authorization).

Основная проблема стандартной реализации – пароль (или его хеш в особо «хитрых» системах) передаются серверу в открытом виде. Т.е. если прослушать канал передачи информации во время самого процесса авторизации взломщик получит заветную пару логин/пароль (или хеш пароля), которая позволит войти в систему с привилегиями пользователя, которому принадлежала эта пара.

Идеальным вариантом было бы использование при аутентификации алгоритмов шифрования с открытым ключом, но это накладывает отпечаток на удобство использования такой системы. Каждому пользователю пришлось бы хранить пару ключей (открытый и закрытый), что для пользователей, приклеивающих свои пароли на стикерах к монитору или к коврику для мышки «роскошь» непозволительная (вспомним один из пунктов заявленных ранее требований). Поэтому отнесем эту мысль к области мечтаний и спустимся с небес на землю – к bypass authorization.

Собственно остановим свое внимание на процессах собственно авторизации и работы пользователе в системе после этого процесса.

Алгоритм

1) Пользователь (далее П.) запрашивает страницу авторизации.

2) Сервер (далее С.) генерирует страницу с полем для ввода логина пользователя и картинку для теста Тьюринга (иначе говоря, captcha; зачем это нужно станет понятно далее), код которой сохраняется в сессионной переменной.

3) П. вводит свой логин и код изображенный на картинке

4) С. ищет в своей базе пользователей информацию о том, логин которого соответствует переданному со стороны клиента. Если такового нет в базе, выводится сообщение об ошибке. Процесс авторизации завершается.
В противоположном случае проверяется правильность введенного кода картинки.
В случае правильного ввода в базе данных создается запись о новом процессе авторизации, ему присваивается некий уникальный номер seq_id. При этом сохраняются данные о IP-адресе клиента и используемом ним браузере. Также генерируется случайная 64-битная последовательность (назовем ее rmask), которая вместе с seq_id и страницей с полем для ввода пароля передается пользователю.

5) Пользователь вводит свой пароль. На клиентской стороне вычисляется его MD5-хеш. Этот хеш приписывается к себе еще раз, т.е. получаем 64-символьную последовательность.
Сопоставляя полученную строку и полученную от сервера маску rmask, выбираются символы, которые соответствуют единицам в маске. К полученной строке добавляется seq_id, полученный от С и из результата вычисляется хеш по алгоритму SHA-1.
seq_id и полученный SHA-1 хеш отправляются серверу.

6) С. ищет в своей базе информацию о процессе авторизации с идентификатором seq_id, присланным от П.
Если соответствующий найден и совпадают IP-адрес и версия браузера с записанными ранее, из базы извлекается MD5-хеш пароля пользователя, записанный туда при регистрации и повторяется процесс, происходивший в пункте 5 на стороне клиента. Полученные SHA-1 слепки сравниваются и, в случае совпадения, процесс аутентификации считается пройденным. Регистрируется новая сессия, создается переменная sid, являющаяся SHA-1 слепком от строки uid+ip+ua+session_id, где uid – внутренний идентификатор пользователя, ip и ua – IP-адрес и версия браузера соответственно, session_id – идентификатор зарегистрированной сессии.
sid передается П. и хранится на стороне С. Также П. передается session_id в открытом виде для дальнейшего нахождения зарегистрированной сессии в базе. Информация о текущем процессе аутентификации удаляется из базы, т.к. больше не нужна.

В дальнейшем сторона пользователя с каждым запросом помимо прочего передает значения sid и session_id. На серверной стороне по значению session_id ищется зарегистрированная сессия, из нее извлекаются данные о IP-адресе, версии браузера и id пользователя, создается SHA-1 слепок и сравнивается с полученным от клиента. Результат сравнения и будет результатом аутентификации.

Примечания

При каждом новом процессе авторизации от клиента передается серверу новый слепок, что с учетом привязки сессии к IP-адресу и браузеру пользователя не дает возможности кражи сессии, т.к. сервер каждый раз требует от пользователя новую выборку.

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

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

Также при таком алгоритме необходимо использовать технологию TTL (Time To Live) для зарегистрированных сессий и процессов авторизации. Это позволяет избежать засорения базы данных. Но при этом придется отказаться от популярной сейчас опции «запомнить меня». Многим ввод пароля при каждом новом посещении неудобен, зато это значительно повышает защищенность системы.

Так как генерация нового случайного числа связана с большими затратами ресурсов, применяем тест Тьюринга, чтобы отличить попытку регистрации живым пользователем от попыток атаки роботами (DDOS). Логика проста: не человек – не авторизируем.

В ряде случаев необходима взаимная авторизация – тогда необходимо использовать SSL, причем сертификат сервера должен(!) быть подписан третьей стороной, которой доверяют как П. так и С., и которая может подтвердить его подлинность. Такими являются, к примеру, VerySign или Thawte.

Заключение

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

При создании протокола я ориентировался на связку Apache+PHP+MySQL, но его работа будет эффективной и в любой другой системе.

Я заведомо не написал ни одной строчки кода, пусть реализация этого алгоритма будет маленьким домашним заданием =).

©Helios, 10.09.2007

PS: Отдельное спасибо VDShark, A110ut и m0nzt3r за конструктивную критику