OAuth
Просматривая веб‑сайты, вы почти наверняка встречали страницы, позволяющие войти с использованием вашей учётной записи в социальной сети. Скорее всего, эта функция построена на популярном фреймворке OAuth 2.0. Он особенно интересен для атакующих, поскольку он чрезвычайно распространён и по своей природе склонен к ошибкам реализации. Это может приводить к целому ряду уязвимостей, позволяющих атакующим получать конфиденциальные пользовательские данные и потенциально полностью обходить аутентификацию.
В этом разделе вы узнаете как выявлять и эксплуатировать некоторые ключевые уязвимости в OAuth 2.0. Если вы еще не знакомы с аутентификацией OAuth, не переживайте, здесь будет достаточно базовой информации, чтобы вы поняли ключевые концепции. Мы также рассмотрим некоторые уязвимости в OpenID Connect. А также рекомендации о том, как защитить ваше приложение от такого рода атак.
Research
Эта тема подготовлена в сотрудничестве с PortSwigger Research, в том числе на основе статьи Hidden OAuth Attack Vectors.

Лабораторные работы
Если вы уже знакомы с базовыми концепциями уязвимостей OAuth и просто хотите попрактиковаться в их эксплуатации на реалистичных, намеренно уязвимых целях, вы можете получить доступ ко всем лабораторным работам по этой теме по ссылке ниже.
Что такое OAuth?
OAuth — это широко используемый фреймворк авторизации, который позволяет веб‑сайтам и веб‑приложениям запрашивать ограниченный доступ к учётной записи пользователя в другом приложении. Критически важно, что OAuth позволяет пользователю предоставить этот доступ, не раскрывая свои учётные данные приложению, делающему запрос. Это значит, что пользователи могут тонко настраивать, какими данными они готовы делиться, вместо того чтобы передавать полный контроль третьей стороне.
Основной процесс OAuth широко используется для интеграции сторонних функций, которые требуют доступа к определенным данным из учетной записи пользователя. Например, приложение может использовать OAuth для запроса доступа к списку контактов вашей электронной почты, чтобы предложить вам людей, с которыми можно связаться. Также этот механизм используется для предоставления сторонних сервисов аутентификации, позволяя пользователям входить с помощью учётной записи, имеющейся на другом сайте.
Note
Хотя OAuth 2.0 — текущий стандарт, некоторые сайты всё ещё используют устаревшую версию 1a. OAuth 2.0 был написан с нуля, а не разработан напрямую из OAuth 1.0. В результате они существенно отличаются. Обратите внимание, что термин «OAuth» в этих материалах относится исключительно к OAuth 2.0.
Как работает OAuth 2.0?
OAuth 2.0 изначально разрабатывался как способ совместного использования доступа к конкретным данным между приложениями. Он работает, определяя ряд взаимодействий между тремя различными сторонами: клиентским приложением, владельцем ресурса и поставщиком сервиса OAuth.
Клиентское приложение — веб‑сайт или веб‑приложение, которое хочет получить доступ к данным пользователя.
Владелец ресурса — пользователь, к чьим данным хочет получить доступ клиентское приложение.
Поставщик сервиса OAuth — веб‑сайт или приложение, контролирующее данные пользователя и доступ к ним. Он поддерживает OAuth, предоставляя API для взаимодействия как с сервером авторизации, так и с сервером ресурсов.
Существует множество способов реализации процесса OAuth. Они известны как «потоки» (flows) или «типы грантов» (grant types). В этой теме мы сосредоточимся на типах грантов «authorization code» и «implicit», поскольку они наиболее распространены. В общих чертах оба эти типа включают следующие этапы:
Клиентское приложение запрашивает доступ к некоторому набору данных пользователя, указывая тип гранта и необходимый вид доступа.
Пользователю предлагается войти в сервис OAuth и явно дать согласие на запрошенный доступ.
Клиентское приложение получает уникальный токен доступа, подтверждающий, что у него есть разрешение пользователя на доступ к запрошенным данным. Точный способ получения токена зависит от типа гранта.
Клиентское приложение использует этот токен доступа для вызова API и получения соответствующих данных с сервера ресурсов.
Прежде чем изучать использование OAuth для аутентификации, важно понять основы базового процесса OAuth. Если вы совсем новичок в OAuth, рекомендуем сначала ознакомиться с деталями типов грантов, которые мы будем рассматривать, прежде чем идти дальше.
Подробнее
Аутентификация по OAuth
Хотя изначально для этого он не предназначался, OAuth эволюционировал и в средство аутентификации пользователей. Например, вы наверняка знакомы с опцией, позволяющей войти с помощью существующей учётной записи в социальной сети, вместо регистрации на самом сайте. В подобных случаях велика вероятность, что всё построено на OAuth 2.0.
Для механизмов аутентификации OAuth базовые потоки остаются в целом такими же; основное отличие — в том, как клиентское приложение использует полученные данные. С точки зрения конечного пользователя результат аутентификации по OAuth в целом напоминает SSO на основе SAML. В этих материалах мы сосредоточимся исключительно на уязвимостях в этом SSO‑подобном варианте использования.
Аутентификация OAuth обычно реализуется следующим образом:
Пользователь выбирает опцию входа через социальную сеть. Клиентское приложение затем использует сервис OAuth сайта социальной сети, чтобы запросить доступ к некоторым данным, по которым оно сможет идентифицировать пользователя. Например, это может быть адрес электронной почты, зарегистрированный в его учётной записи.
Получив токен доступа, клиентское приложение запрашивает эти данные у сервера ресурсов, обычно у специальной конечной точки
/userinfo.После получения данных клиентское приложение использует их вместо имени пользователя, чтобы выполнить вход. Токен доступа, полученный от сервера авторизации, часто используется вместо традиционного пароля.
Простой пример этого можно увидеть в следующей лабораторной работе. Просто выполните вход через опцию «Log in with social media», проксируя трафик через Burp, затем изучите последовательность запросов OAuth в истории прокси. Вы можете войти, используя учётные данные wiener:peter. Обратите внимание, что эта реализация намеренно уязвима — позже мы покажем, как её эксплуатировать.
Как возникают уязвимости аутентификации OAuth?
Уязвимости аутентификации OAuth частично возникают потому, что спецификация OAuth по замыслу относительно расплывчата и гибка. Хотя есть несколько обязательных компонентов, необходимых для базовой функциональности каждого типа гранта, подавляющее большинство аспектов реализации полностью опционально. Это включает множество настроек конфигурации, необходимых для безопасной обработки пользовательских данных. Проще говоря, есть широкое поле для появления плохих практик.
Ещё одна ключевая проблема OAuth — общий недостаток встроенных функций безопасности. Безопасность почти полностью зависит от того, что разработчики используют правильную комбинацию параметров конфигурации и накладывают собственные дополнительные меры безопасности сверху, такие как строгая валидация входных данных. Как вы, вероятно, уже поняли, это довольно объёмная тема, и легко допустить ошибки, если у вас мало опыта с OAuth.
В зависимости от типа гранта, через браузер пользователя также могут передаются чувствительные данные, что создаёт различные возможности для их перехвата.
Как распознать аутентификацию OAuth
Опознать использование аутентификации OAuth относительно просто. Если вы видите опцию входа с использованием учётной записи с другого сайта, это сильный признак использования OAuth.
Самый надёжный способ распознать аутентификацию OAuth — проксировать трафик через Burp и проверить соответствующие HTTP‑запросы при использовании этой опции входа. Независимо от типа гранта OAuth первый запрос потока всегда будет запросом к конечной точке /authorization, содержащим ряд параметров запроса, специфичных для OAuth. В частности, обратите внимание на параметры client_id, redirect_uri и response_type.
Например, запрос авторизации обычно выглядит примерно так:
Разведка
Базовая разведка используемого сервиса OAuth может навести вас на правильные мысли при выявлении уязвимостей.
Само собой разумеется, что вы должны изучить различные HTTP‑запросы, составляющие поток OAuth. Позже мы обсудим конкретные вещи, на которые стоит обращать внимание. Если используется внешний сервис OAuth, вы сможете определить конкретного поставщика по имени хоста, на который отправляется запрос авторизации. Поскольку эти сервисы предоставляют публичный API, часто доступна подробная документация, из которой можно почерпнуть много полезной информации, например точные названия конечных точек и используемые параметры конфигурации.
Зная домен сервера авторизации, всегда попробуйте отправить GET запрос на следующие стандартные конечные точки:
/.well-known/oauth-authorization-server/.well-known/openid-configuration
Они часто возвращают конфигурационный JSON‑файл с ключевой информацией, например о дополнительных поддерживаемых возможностях. Это может указать на более широкую поверхность атаки и поддерживаемые функции, которые не упомянуты в документации.
Эксплуатация уязвимостей аутентификации OAuth
Уязвимости могут возникать как в реализации OAuth на стороне клиентского приложения, так и в конфигурации самого сервиса OAuth. В этом разделе мы покажем, как эксплуатировать некоторые из наиболее распространённых уязвимостей в обоих контекстах.
Уязвимости в клиентском приложении
Уязвимости в клиентском приложении OAuth
Клиентские приложения часто используют авторитетный и надежный сервис OAuth, хорошо защищённый от широко известных эксплойтов. Однако их собственная часть реализации может быть менее безопасной.
Как уже отмечалось, спецификация OAuth относительно свободно определена. Это особенно справедливо для реализации на стороне клиентского приложения. В потоке OAuth много движущихся частей, с множеством дополнительных параметров и настроек в каждом типе гранта, что даёт широкие возможности для неправильной конфигурации.
Некорректная реализация implicit grant
Из‑за риска, связанного с передачей токенов доступа через браузер, тип гранта implicit обычно рекомендуется для одностраничных приложений (SPA). Однако его часто используют и в классических клиент‑серверных веб‑приложениях из‑за относительной простоты.
В этом потоке токен доступа отправляется от сервиса OAuth клиентскому приложению через браузер пользователя как фрагмент URL. Клиентское приложение затем получает токен с помощью JavaScript. Проблема в том, что если приложению нужно поддерживать сессию после закрытия страницы, где‑то нужно хранить текущие данные пользователя (обычно идентификатор пользователя и токен доступа).
Чтобы решить это, клиентское приложение часто отправляет эти данные на сервер POST запросом, а затем назначает пользователю сессионный cookie, фактически выполняя вход. Этот запрос примерно эквивалентен запросу отправки формы, который может отправляться в рамках классического входа по паролю. Однако в данном сценарии у сервера нет секретов или паролей для сравнения с отправленными данными, поэтому они считаются неявно доверенными.
В implicit‑потоке этот POST запрос открыт атакующему через его браузер. В результате, если клиентское приложение должным образом не проверяет, что токен доступа соответствует остальным данным запроса, возникает серьёзная уязвимость. В таком случае атакующий может просто изменить параметры, отправляемые на сервер, чтобы выдать себя за любого пользователя.
Ошибочная защита от CSRF
Хотя многие компоненты потоков OAuth опциональны, некоторые настоятельно рекомендуется использовать, если нет серьёзной причины этого не делать. Один из таких примеров — параметр state.
В идеале параметр state должен содержать непредсказуемое значение, например хеш чего‑то, привязанного к сессии пользователя при начальной инициации потока OAuth. Это значение затем передаётся туда‑обратно между клиентским приложением и сервисом OAuth в качестве CSRF‑токена для клиентского приложения. Поэтому, если вы замечаете, что запрос авторизации не отправляет параметр state, это чрезвычайно интересно с точки зрения атакующего. Потенциально это означает, что он может сам инициировать поток OAuth, а затем заставить браузер пользователя завершить его, аналогично традиционной CSRF атаке. Последствия могут быть серьёзными, в зависимости от того, как клиентское приложение использует OAuth.
Рассмотрим сайт, позволяющий пользователям входить либо традиционным механизмом на основе пароля, либо привязав учётную запись к профилю в соцсети через OAuth. В этом случае, если приложение не использует параметр state, атакующий потенциально может захватить учётную запись жертвы в клиентском приложении, привязав её к своей собственной учётной записи в соцсети.
Обратите внимание, что если сайт позволяет вход только через OAuth, параметр state, возможно, менее критичен. Однако его отсутствие всё равно может позволить атакующим строить login‑CSRF атаки, при которых пользователь обманом входит в учётную запись атакующего.
Утечка кодов авторизации и токенов доступа
Возможно, самая распространенная уязвимость на основе OAuth — когда конфигурация сервиса OAuth позволяет атакующим красть коды авторизации или токены доступа, связанные с учётными записями других пользователей. Украв действительный код или токен, атакующий может получить доступ к данным жертвы. В итоге это может полностью скомпрометировать её учётную запись.
В зависимости от типа гранта код или токен отправляется через браузер жертвы на конечную точку /callback, указанную в параметре redirect_uri запроса авторизации. Если сервис OAuth неправильно валидирует этот URI, атакующий может построить атаку, подобную CSRF, заставив браузер жертвы инициировать поток OAuth, который отправит код или токен на подконтрольный атакующему redirect_uri.
В случае потока authorization code атакующий может украсть код жертвы до его использования. Затем он может отправить этот код на легитимную конечную точку /callback клиентского приложения (исходный redirect_uri), чтобы получить доступ к учётной записи пользователя. В этом сценарии атакующему даже не нужно знать клиентский секрет или итоговый токен доступа. Пока у жертвы есть действительная сессия с сервисом OAuth, клиентское приложение просто завершит обмен кодом/токеном от имени атакующего и выполнит вход в учётную запись жертвы.
Обратите внимание, что защита state или nonce не обязательно предотвращает эти атаки, поскольку атакующий может генерировать новые значения со своего браузера.
Более безопасные серверы авторизации требуют также отправки параметра redirect_uri при обмене кода. Тогда сервер может проверить соответствие этому значению из первоначального запроса авторизации и отклонить обмен, если они не совпадают. Поскольку это происходит в межсерверных запросах по защищённому каналу, атакующий не может контролировать параметр redirect_uri.
Ошибочная валидация redirect_uri
Из‑за атак, рассмотренных в предыдущей лабораторной работе, лучшая практика для клиентских приложений — предоставлять белый список подлинных callback‑URI при регистрации в сервисе OAuth. Тогда, получив новый запрос, сервис OAuth сможет валидировать параметр redirect_uri по этому белому списку. В таком случае указание внешнего URI, скорее всего, приведёт к ошибке. Однако иногда валидацию можно обойти.
Проводя аудит потока OAuth, экспериментируйте с параметром redirect_uri, чтобы понять, как он валидируется. Например:
Некоторые реализации допускают диапазон подкаталогов, проверяя только, начинается ли строка с правильной последовательности символов, то есть с утверждённого домена. Попробуйте удалять или добавлять произвольные пути, параметры запроса и фрагменты, чтобы увидеть, что можно изменить, не вызвав ошибку.
Если вы можете дописать дополнительные значения к параметру
redirect_uri, возможно, получится эксплуатировать расхождения в разборе URI различными компонентами сервиса OAuth. Например, попробуйте такую технику:Если вы не знакомы с этими техниками, рекомендуем прочитать материалы о том, как обходить типичные защиты от SSRF и CORS.
Иногда встречаются уязвимости загрязнения параметров на стороне сервера. На всякий случай попробуйте отправить дублирующиеся параметры
redirect_uri, например так:Некоторые серверы также по‑особому относятся к URI на
localhost, поскольку они часто используются при разработке. В некоторых случаях любые redirect‑URI, начинающиеся сlocalhost, могут случайно быть разрешены и в проде. Это может позволить обойти валидацию, зарегистрировав доменное имя видаlocalhost.evil-user.net.
Важно не ограничиваться тестированием одного параметра redirect_uri в изоляции. На практике вам часто придётся экспериментировать с разными комбинациями изменений нескольких параметров. Иногда изменение одного параметра влияет на валидацию других. Например, смена response_mode с query на fragment иногда полностью меняет обработку redirect_uri, позволяя передать URI, которые иначе были бы заблокированы. Точно так же, если поддерживается режим web_message, он часто допускает более широкий диапазон поддоменов в redirect_uri.
Кража кодов и токенов через прокси‑страницу
Против более защищённых целей вы можете обнаружить, что как ни старайтесь, не удаётся отправить внешний домен в качестве redirect_uri. Но это не значит, что пора сдаваться. К этому моменту вы должны уже неплохо понимать, какими частями URI можно манипулировать. Теперь нужно использовать эти знания, чтобы выйти на более широкую поверхность атаки в самом клиентском приложении. Попытайтесь выяснить, можно ли изменить параметр redirect_uri так, чтобы он указывал на любые другие страницы на домене из белого списка.
Попробуйте найти способы добиться доступа к различным поддоменам или путям. Например, URI часто расположен на специфическом для OAuth пути, таком как /oauth/callback, у которого вряд ли будут интересные подкаталоги. Однако вы можете воспользоваться приёмами обхода каталогов, чтобы указать произвольный путь.
Что‑то вроде:
Может быть интерпретировано на бэкенде как:
Определив, какие ещё страницы можно использовать как redirect‑URI, проверьте их на дополнительные уязвимости, которые можно потенциально применить для утечки кода или токена. Для authorization code нужно найти уязвимость, дающую доступ к параметрам запроса, а для implicit grant — возможность извлечь фрагмент URL.
Одной из самых полезных уязвимостей для этой цели является открытая переадресация. Её можно использовать как прокси, чтобы переслать жертв вместе с их кодом или токеном на подконтрольный атакующему домен, где вы можете разместить любой вредоносный скрипт.
Обратите внимание, что для типа implicit кража токена доступа позволяет не только войти в учётную запись жертвы в клиентском приложении. Поскольку весь поток implicit проходит через браузер, вы можете также использовать токен для собственных вызовов API к серверу ресурсов сервиса OAuth. Это может позволить извлечь конфиденциальные пользовательские данные, недоступные из веб‑интерфейса клиентского приложения.
В дополнение к open redirection ищите любые другие уязвимости, позволяющие извлечь код или токен и отправить их на внешний домен. Хорошие примеры:
Опасный JavaScript, обрабатывающий параметры запроса и фрагменты URL. Например, небезопасные скрипты веб‑сообщений отлично подходят для этого. В некоторых сценариях вам придётся выявить более длинную цепочку гаджетов, позволяющую пропустить токен через серию скриптов и в итоге отправить его на внешний домен.
Уязвимости XSS. Хотя XSS‑атаки сами по себе могут иметь большой эффект, окно времени доступа атакующего к сессии пользователя обычно невелико — пока вкладка не закрыта или пользователь не ушёл со страницы. Поскольку атрибут HTTPOnly часто используется для сессионных cookie, атакующий обычно не может обратиться к ним напрямую через XSS. Однако, украв OAuth‑код или токен, атакующий получает доступ к учётной записи пользователя в своём браузере. Это даёт куда больше времени для изучения данных и выполнения вредоносных действий, значительно повышая серьёзность XSS‑уязвимости.
HTML‑инъекция. В случаях, когда нельзя внедрить JavaScript (например, из‑за CSP или строгой фильтрации), вы всё ещё можете использовать простую HTML‑инъекцию, чтобы украсть коды авторизации. Если вы можете направить параметр redirect_uri на страницу, в которую удаётся внедрить собственный HTML‑контент, возможно украсть код через заголовок Referer. Например, рассмотрим элемент img:
<img src="evil-user.net">. При попытке загрузить это изображение некоторые браузеры (например, Firefox) отправят в заголовке Referer полный URL, включая строку запроса.
Ошибочная проверка scope
В любом OAuth‑потоке пользователь должен одобрить запрошенный доступ на основе scope, определённой в запросе авторизации. Итоговый токен позволяет клиентскому приложению обращаться только к области, одобренной пользователем. Но в некоторых случаях атакующий может повысить токен доступа (украденный или полученный с помощью вредоносного клиентского приложения), добавив права из‑за ошибочной проверки на стороне сервиса OAuth. Процесс зависит от типа гранта.
Повышение scope: authorization code flow
Для гранта authorization code данные пользователя запрашиваются и передаются по защищённой межсерверной связи, которой сторонний атакующий обычно не может напрямую манипулировать. Однако можно добиться того же, зарегистрировав собственное клиентское приложение у сервиса OAuth.
Например, предположим, что вредоносное клиентское приложение атакующего изначально запросило доступ к адресу электронной почты пользователя с областью openid email. После того как пользователь одобряет запрос, вредоносное приложение получает код авторизации. Поскольку атакующий контролирует своё приложение, он добавляет ещё один параметр scope в запрос обмена код/токен, включив дополнительную область profile:
Если сервер не валидирует это значение относительно scope из начального запроса авторизации, он иногда сгенерирует токен доступа с новой областью и отправит его клиентскому приложению атакующего:
Атакующий затем может использовать своё приложение для необходимых вызовов API, чтобы получить доступ к данным профиля пользователя.
Повышение scope: implicit flow
Для гранта implicit токен доступа отправляется через браузер, а значит, атакующий может красть токены, связанные с невинными клиентскими приложениями, и использовать их напрямую. Получив токен доступа, он может отправить обычный запрос из браузера к конечной точке /userinfo сервиса OAuth, вручную добавив новый параметр scope.
Идеально сервис OAuth должен валидировать scope относительно того, который использовался при генерации токена, но это происходит не всегда. Пока скорректированные разрешения не превышают уровень доступа, ранее предоставленный этому клиентскому приложению, атакующий может потенциально получить доступ к дополнительным данным без повторного одобрения пользователем.
Неподтверждённая регистрация пользователя
При аутентификации через OAuth клиентское приложение неявно предполагает, что информация, хранящаяся у провайдера OAuth, корректна. Это предположение может быть опасным.
Некоторые сайты, предоставляющие сервис OAuth, позволяют регистрировать аккаунт без подтверждения всех данных, включая адрес электронной почты. Атакующий может воспользоваться этим, зарегистрировав аккаунт у провайдера OAuth с теми же данными, что и у целевого пользователя, например с известным адресом электронной почты. Клиентские приложения затем могут позволить атакующему войти как жертва через этот мошеннический аккаунт у провайдера OAuth.
Расширение OAuth с помощью OpenID Connect
При использовании OAuth для аутентификации часто расширяют слоем OpenID Connect, который добавляет функции, связанные с идентификацией и аутентификацией пользователей. Подробное описание этих возможностей и дополнительные лабораторные работы по уязвимостям, которые они могут вводить, см. в теме OpenID Connect.
Подробнее
Предотвращение уязвимостей аутентификации OAuth
Для разработчиков мы предоставили рекомендации о том, как избежать внесения этих уязвимостей в ваши веб‑сайты и приложения.
Last updated