Небольшое исследование на тему безопасной авторизации

в 6:47, , рубрики: авторизация, Безопастность, Веб-разработка, хэширование

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

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

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

После каждого запроса хеш сессии можно менять, в особых случаях это можно делать по таймеру через JavaScript. Это сведет на нет вероятность взлома через кражу куки. Надежный пароль плюс высокая сложностью хеширования пароля сведет на нет смысл кражи базы данных. Все вроде бы хорошо, но смущает один момент: пароль по сети передается в открытом виде, до попадания в серверный скрипт пароль никак не защищен. Поиск решения этой проблемы оказался интересней. Нашлось довольно много закрученных вариантов. Если рассматривать варианты защиты только от перехвата пароля, то наиболее простой и надежный способ выглядит примерно так:

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

хеш_клиентской_части = функция_хеширования(функция_хеширования(пароль) + случайная_строка_из_куки)

Сервер достает из базы данных хеш пароля, хеш объединяется со случайной строкой и еще раз хешируется.

хеш_серверной_части = функция_хеширования(хеш_пароля_из_базы + случайная_строка_из_куки)

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

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

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

for (i = 0; i < случайное_число_от_сервера; i++) {
	пароль = функция_хеширования(пароль)
}

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

количество_итераций_серверной_функции = постоянное_число_из_константы - случайное_число_из_куки
for (i = 0; i < количество_итераций_серверной_функции; i++) {
	хеш_от_клиентской_части = функция_хеширования(хеш_от_клиентской_части)
}

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

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

В итоге в проекте был реализован последний вариант. Спасибо за внимание и приятного всем кодинга.

Автор: Fibril

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js