How Cookie-Based Authentication Works in the Takes Framework

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

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

Сначала давайте посмотрим, как это работает. Кроме того, давайте посмотрим, как я считаю, что оно должно работать.

Шаг первый: Пользователь вводит электронную почту и пароль, а затем нажимает “Отправить”. Сервер получает POST-запрос с этой информацией внутри:

Сервер сравнивает предоставленную информацию со своими записями и принимает решение о дальнейших действиях. Если информация недействительна, сервер возвращает ту же страницу входа, запрашивая повторный ввод данных. Если информация действительна, сервер возвращает что-то вроде этого:

Поскольку код состояния ответа равен 303, браузер переходит на страницу, указанную в заголовке Location и открывает главную страницу сайта. Вот что он отправляет на сервер:

Сервер получает мою электронную почту из заголовка «Cookie» и понимает, что это снова я! Нет необходимости запрашивать пароль снова. Сервер доверяет информации из cookie. Вот и все. В этом и заключается аутентификация на основе cookie.

Правильно, а что насчет безопасности? Если сервер доверяет любому запросу браузера с электронной почтой пользователя в заголовке Cookie, то любой сможет отправить мою электронную почту из другого места и получить доступ к моему аккаунту.

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

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

Все это звучит хорошо, но что, если кто-то перехватит трафик между сервером и браузером и получит доступ к правильно зашифрованному email-куки? В этом случае злоумышленник сможет использовать то же самое куки для аутентификации, даже не зная его содержимого. Сервер будет доверять этой информации и позволит этому человеку получить доступ к моему аккаунту. Такой тип атаки называется атакой “человек посередине” (MITM). Чтобы предотвратить это, мы должны использовать HTTPS и сообщить браузеру, что куки является конфиденциальным и никогда не должно передаваться на сервер без SSL-шифрования. Это делается с помощью дополнительного флага в заголовке Set-Cookie.

Есть еще один тип атаки, связанный с аутентификацией на основе кукисов, основанный на возможности браузера выставлять все куки, связанные с веб-страницей, для выполняемого внутри нее JavaScript. Злоумышленник может внедрить некоторый вредоносный код JavaScript на страницу (не спрашивайте меня, как … это произойдет только если ваша вся обработка HTML выполнена неправильно), и этот код получит доступ к куке. Затем код отправит куку куда-то еще, чтобы злоумышленник мог ее собрать. Этот тип атаки называется межсайтовый скриптинг (XSS). Чтобы предотвратить это, для заголовка Set-Cookie существует еще один флаг, называемый HttpOnly:

Наличие этого флага скажет браузеру, что данный cookie может быть передан обратно на сервер только через HTTP-запросы. JavaScript не будет иметь к нему доступ.

Вот как это механизм аутентификации на основе кук реализован в фреймворке Takes. Весь фреймворк состоит из takes, которые принимают запросы и генерируют ответы (эта статья подробнее объясняет фреймворк). Когда запрос поступает, мы должны найти аутентификационную куку в заголовке Cookie и перевести ее в учетные данные пользователя. Когда ответ отправляется, мы должны добавить заголовок Set-Cookie с зашифрованными учетными данными пользователя. Вот и все. Всего два шага.

Предположим, у нас есть страница аккаунта, которая должна показывать текущий баланс пользователя:

Сразу после получения request мы должны извлечь идентификатор пользователя, закодированный в аутентификационном cookie. Чтобы сделать этот механизм повторно используемым, у нас есть декоратор TkAuth, который оборачивает существующий take, декодирует входящий cookie и добавляет новый заголовок TkAuth к запросу с информацией об идентификации пользователя.

Опять же, когда TkAuth получает запрос с аутентификационным cookie внутри, он запрашивает у pass расшифровать cookie и вернуть либо действительный Identity, либо Identity.ANONYMOUS.

Затем, когда ответ возвращается обратно в браузер, TkAuth запрашивает у pass закодировать идентификацию обратно в строку и добавить Set-Cookie в ответ.

PsCookie использует экземпляр Codec для выполнения этих операций обратного и прямого кодирования.

Когда наш TkAccount take хочет получить идентификацию текущего аутентифицированного пользователя из запроса, он может использовать RqAuth, утилитный декоратор Request:

Декоратор RqAuth использует заголовок, добавленный PsCookie, для аутентификации пользователя и создания объекта Identity.

Этот механизм действительно очень расширяемый и “компонуемый”. Допустим, мы хотим пропустить аутентификацию во время интеграционного тестирования. Вот как:

PsChain реализует Pass и пытается аутентифицировать пользователя, запрашивая все инкапсулированные пропуски один за другим. Первый в цепочке - PsFake. Используя один логический аргумент в своем конструкторе, он принимает решение, возвращать фейковую идентификацию или ничего не возвращать. С помощью всего лишь одного логического триггера мы можем отключить весь механизм аутентификации в приложении.

Предположим, вы хотите аутентифицировать пользователей через Facebook OAuth. Вот как это сделать:

Когда пользователь нажимает на ссылку входа на вашем сайте, браузер переходит на facebook.com, где происходит проверка его или ее личности. Затем Facebook возвращает ответ 302 с перенаправлением и заголовком Location, установленным на URL, который мы указали в ссылке входа. Ссылка должна содержать что-то вроде этого: ?PsByFlag=PsFacebook. Это сообщит PsByFlag, что этот запрос аутентифицирует пользователя.

Вот как мы можем реализовать механизм выхода:

Теперь мы можем добавить ?PsByFlag=PsLogout к любой ссылке на сайте, и это разлогинит текущего пользователя.

Вы можете увидеть, как все это работает в реальном приложении, посмотрев на класс TkAppAuth в Rultor.

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-12-27 at 04:46

sixnines availability badge   GitHub stars