Содержание статьи
- Что такое OAuth и как он появился
- Разбираем устройство протокола на реальном примере
- Authorization Request
- Consent Screen и Authorization Response
- Access token request
- Access token grant
- API Call и Resource Grant
- Implicit Grant Flow
- OAuth и аутентификация
- Лаба: Authentication bypass via OAuth implicit flow
- CSRF-атаки на OAuth
- Лаба: Forced OAuth profile linking
- Выводы
Думаю, не нужно объяснять, что такое аутентификация на сайте. Если ты хочешь искать работу, оформлять заказы в интернете или пользоваться государственными услугами, тебя попросят подтвердить, что именно ты владелец аккаунта. И даже когда это не особенно нужно, разработчики все равно заставят тебя авторизоваться — просто чтобы было удобнее собирать о тебе данные. Но статья не об этичности, поэтому перейдем к самой теме.
Чтобы каждый раз не приходилось вбивать свою электронную почту, дожидаться прихода на нее сообщения с кодом или ссылкой для подтверждения аккаунта и придумывать сложный пароль, достаточно однажды зарегистрироваться на одном из мегапопулярных сайтов и входить под созданной учетной записью на остальных.
Такая технология есть, она давно известна и называется OAuth. Именно ее используют соцсети и прочие крупные сервисы, когда предлагают авторизоваться через них.
Этот метод входа стал настолько обыденным, что многие не задумываются о том, как он устроен, — а зря, ведь дьявол, как известно, кроется как раз в деталях. Мелочей здесь настолько много, что пришлось разделить гигантский материал по этой теме на три части.
В этой и следующих статьях мы пошагово разберем, как работают технологии OAuth и OpenID Connect, в чем отличия разных версий протоколов и какие риски несут эти технологии при неправильной реализации. Мы также обязательно пройдем лаборатории PortSwigger, чтобы научиться эксплуатировать уязвимости на практике.
Начнем с базовых атак на OAuth.
Что такое OAuth и как он появился
На неофициальном сайте Аарона Парецкого (Aaron Parecki), который собирает информацию об OAuth, приведено краткое, простое, емкое и довольно понятное определение:
OAuth 2.0 — это способ авторизации, с помощью которого пользователи могут предоставлять веб‑сайтам или приложениям доступ к своей информации, не передавая пароли.
Не надо вводить ничего лишнего: нажал кнопку, выдал согласие — и у приложения тут же оказались твои данные. В современном вебе OAuth поддерживает практически каждый крупный сайт, и действительно многие пользователи выбирают его вместо классических механизмов.
Сам протокол существует уже давно. Идея создания OAuth возникла во время разработки Twitter OpenID (не стоит путать с OpenID Connect), в ноябре 2007 года, когда компания создавала интеграцию с Ma.gnolia. Это канувший в Лету социальный сервис закладок, где люди могли делиться сохраненными ссылками на сайты.
Целью интеграции Twitter и Ma.gnolia было дать возможность Ma.gnolia использовать виджеты информационной панели Twitter. Блейн Кук, на то время ведущий разработчик в Twitter, заметил, что открытого механизма, который делегировал бы доступ к API, просто не существует.
На тот момент уже были похожие решения, о которых ты, наверное, мог и не слышать: AuthSub (Google), BBAuth (Yahoo), OpenAuth (AOL), Windows Live ID Web Authentication (Microsoft) и Facebook Auth (Meta). Но они были проприетарными и потому не годились. Еще более древние методы вроде передачи паролей небезопасны и неудобны для таких сценариев. OAuth был предложен как решение этой проблемы.
Вот как лаконично описывают OAuth разработчики Джон Панзер и Эран Хаммер‑Лахав, которые участвовали в создании открытой спецификации:
OAuth — это своего рода ключ доступа ко всем вашим веб‑сервисам. Ключ парковщика позволяет вам дать парковщику возможность припарковать ваш автомобиль, но не дает ему возможности попасть в багажник, проехать более двух миль или ограничить обороты вашего дорогого немецкого автомобиля. Точно так же ключ OAuth позволяет вам дать веб‑агенту возможность проверять вашу веб‑почту, но не возможность притворяться вами и отправлять почту всем знакомым из вашей адресной книги.
Финальный вариант реализации версии OAuth 1.0 был утвержден 4 декабря 2007 года (анонс можно найти в веб‑архиве), а уже в 2008 году этот протокол поддержал у себя Google, и аудитория протокола начала расти в геометрической прогрессии. К 2010 году Twitter заставил все сторонние приложения использовать свою реализацию OAuth 1.0.
Но за год до этого события, в 2009 году, была обнаружена атака с фиксацией сессии, позволявшая злоумышленникам получать доступ к чужим ресурсам. В результате этого аудита была разработана немного улучшенная версия протокола — OAuth 1.0a, в которой добавили параметры oauth_callback
и oauth_verifier
для защиты от таких атак.
Однако это не решило остальные проблемы OAuth 1.0 — небезопасность и неудобство использования. Разработчики жаловались, что первый OAuth требовал слишком больших криптографических вычислений на стороне клиента — запросы нужно было подписывать с помощью HMAC. Многим такая криптография срывала сроки разработки и серьезно усложняла тестирование.
За три года с момента зарождения первой версии протокола разработчики придумали более простое на уровне реализации, но более совершенное функционально решение, которое привносило много нововведений и которое стали называть OAuth 2.0. Этот протокол появился в 2010 году, а его последняя версия, в качестве RFC 6749, была опубликована в октябре 2012 года.
Разбираем устройство протокола на реальном примере
Чтобы было понятнее, давай использовать реальный пример. OAuth поддерживает несколько Flow, или «потоков», которые определяют, как именно происходит обмен информацией между клиентом, ресурсным сервером и сервером авторизации (с этими понятиями мы познакомимся немного позже), и самый используемый в современном вебе на десктопных сайтах — это Authorization Code Flow. С него и начнем.
Существует известный сайт legacy.midjourney.com, который позволяет создавать изображения с помощью генеративной нейросети.
Чтобы пользоваться его функциями и иметь возможность создавать картинки, надо войти в свой аккаунт, а модель авторизации построена как раз с помощью технологии OAuth.
В OAuth есть четыре сущности:
- Resource Owner (владелец ресурса) — под ресурсом подразумеваются различные данные пользователя, это может быть почта, номер телефона, имя, никнейм и так далее. Владелец ресурса — тот пользователь, который владеет этими данными;
- Application (приложение) или Client (клиент) — само приложение, которое требует доступ к ресурсам пользователя. В нашем примере это сервис Midjourney, который хочет получить наш юзернейм, аватарку и адрес электронной почты, на что требуется согласие от нас;
- Authorization Server (авторизационный сервер) — сервер, который выдает OAuth-токены клиенту после согласия владельца ресурса. На сайте Midjourney в качестве авторизационного сервиса выступают Discord и Gmail;
- Resource Server (сервер ресурсов) — это сайт, где хранятся почта, номер телефона, имя пользователя и другие данные, которые хочет получить клиент. Мы будем входить через Discord (на котором мы уже зарегистрировались когда‑то), а значит, Discord и будет выступать в качестве этого сервера.
Схематично Authorization Code Flow в OAuth выглядит следующим образом.
Когда я описывал сервер авторизации и сервер ресурсов, ты мог заметить, что в нашем примере это в обоих случаях Discord. Здесь стоит отметить, что действительно в качестве этих серверов очень часто выступают одни и те же сервисы, но с разными эндпоинтами.
Это отлично показывает схема из документации Microsoft.
Пугаться обилию информации не стоит, мы разберем каждый из пунктов по порядку.
Authorization Request
Первый этап называется Authorization Request — запрос на авторизацию.
- Пользовать нажимает на кнопку Sign In на главной странице сайта, чтобы войти.
-
Сайт показывает поп‑ап с выбором социальной сети, через которую пользователь хочет войти.
Мы выбираем Continue with Discord, приложение Midjourney генерирует ссылку на Discord и редиректит нас по ней. Там мы встречаем форму с просьбой войти в свой аккаунт.
При этом URL, по которому мы перешли, — это не сырая ссылка на вход, вроде /
. Она особенна тем, что содержит множество параметров, которые были сгенерированы приложением, и выглядит следующим образом:
<https://discord.com/login>?redirect_to=/oauth2/authorize
?response_type=code
&client_id=936929561302675456
&redirect_uri=https://www.midjourney.com/__/auth/handler
&state=AMbdmDkycu0e3INVMzD9TaBJsUz4DqLki0MEElniTdiomtU7ejHQwa-zsdFLI3lv11Dlz0syNqa-sQ_fO9vwS_buX5sfKH_JjP1GJfgq8P0yzkAwTKOFRgZgp1Trz61FhuNd99rep6mYA_0NZniAmHeU31AHLer3ENc9UYhlPv3F0d10TtqAo3jrHFTDnzmWBoryBJbuP1dHH7fmo-UKkqedWNxmSNnOqOIE2erMiwibVnP3bhpWZKH-ka0UB6FesAGOGyaNKZG1KY92X8Rai5ceovEDCRId9vW2q_GLwVTixPua1vD1ChLxPi7QgIiRQCk
&scope=identify email guilds.join guilds.members.read role_connections.write
&context_uri=https://www.midjourney.com
Разберем каждый из параметров:
-
client_id
— случайное значение, которое генерирует Discord или любой другой сервер авторизации. Этот ID выдается сайту, в данном случае Midjourney. Для каждого из сервисов‑клиентов это значение уникально. Оно нужно для того, чтобы Discord понимал, что за приложение запрашивает данные его пользователя, то есть для идентификации; -
redirect_uri
— URI, на который Discord должен перенаправить пользователя после успешной авторизации. В данном случае он должен вернуть его обратно на сайт Midjourney. Несколько уязвимостей завязано на неправильной обработке этого параметра, их мы рассмотрим позже; -
state
— уникальное значение, которое генерируется на каждую сессию авторизации, чтобы защититься от CSRF-атак. В одной из лабораторий мы тоже научимся их эксплуатировать для кражи чужого аккаунта; -
response_type
— если OAuth — это протокол авторизации, то параметрresponse_type
говорит о том, какой флоу этого протокола будет использоваться: их несколько, и они немного отличаются друг от друга. Сейчас приложение подставляет сюда значениеcode
, но тут также может бытьtoken
илиid
;token -
scope
— это те данные или эндпоинты, на которые клиентское приложение Midjourney хочет получить у сервера ресурсов доступ.
В данном случае параметры следующие:
-
identify
открывает приложению возможность обращаться к эндпоинту/
Discord, чтобы получить информацию о пользователе, но без email;users/ @me -
email
— добавляет согласие на выдачу email. Предыдущий эндпоинт теперь будет возвращать и его тоже; -
guilds
— нужен, чтобы клиент мог обращаться к эндпоинту/
, который возвращает имя пользователя, его ID, баннер и другие параметры.users/ @me/ guilds
Помимо identify
, email
и guids
, есть и другие атрибуты для параметра scope
, c ними можно ознакомиться в документации Discord.
Consent Screen и Authorization Response
После того как мы вошли, нас встречает экран‑уведомление, который называется Consent Screen. В нем говорится о том, что клиент Midjourney хочет получить доступ к данным о нас.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»