なぜ危ないのか
OAuth callback URLとは、認可サーバーが認証完了後にブラウザをリダイレクトするURLだ。このURLがHTMLに書かれており、かつそのHTMLが公開されている場合、攻撃者はredirect_uriを本物に偽装した認可リクエストを送り、被害者のアクセストークンを自分のエンドポイントで受け取るフローを構成できる可能性がある。
特に問題なのは、AIが動くデモを優先するために、callback URLをHTMLにハードコードし、さらにトークン取得のfetchリクエストまでフロントエンドで実装してしまうケースだ。この場合、HTMLを受け取った第三者がブラウザの開発者ツールでネットワークタブを確認するだけで、トークン取得の全プロセスが見えてしまう。
テスト用に作ったHTMLが、何らかの経緯で本番に使われることもある。「デモ用に作ったものをそのまま流用した」というのは事故の典型的なパターンで、テスト用のclient_idと本番用のclient_idを同じHTMLで使い回すことで、意図せずテスト用の設定が本番環境に混入することになる。
- 相手がログインなしで開ける状態か確認する
- PCとスマホで最低1回ずつ表示を確認する
- 内部情報・個人情報・不要な外部送信が残っていないか見る
- レビュー期限と修正時の差し替え方を決めておく
ソースで見る場所
HTMLのscriptブロックまたは読み込まれるJSファイルで、fetchまたはXMLHttpRequestでトークンエンドポイントにリクエストしている箇所を探す。`grep -n 'token\|grant_type\|authorization_code' output.html`でヒットした行を確認し、`client_secret`が含まれるリクエストボディが組み立てられていないかをチェックする。
OAuthの認可URLを構成しているコードを探す。`response_type=code`や`scope=`といったパラメータを文字列として組み立てている箇所が見つかったら、そこに使われている`client_id`の値を控えておき、OAuth Providerのコンソールで誰のアカウントで登録されたアプリかを確認する。
コールバック処理を担う関数またはeventListenerを探す。URLハッシュやパラメータから`code=`や`access_token=`を取り出す処理がある場合、その後の処理でトークンをlocalStorageや外部APIに送信していないかを確認する。送信先が自社のドメイン以外を含む場合は危険信号だ。
- HTML内の外部script・form action・iframeを確認する
- APIキーやトークンらしき文字列がないか検索する
- 画像・CSS・JSのパスが公開後も解決できるか見る
- noindexと認証を混同せず、必要なら両方設定する
安全に共有する設定
OAuthフローを含むHTMLを共有する場合は、テスト専用のOAuthアプリをProviderコンソールで作成し、redirect_uriをローカルホストまたはテスト用ドメインのみに限定する。本番用のclient_idは使わず、テスト用アプリのredirect_uriに実際の公開URLを追加しないことで、共有HTMLを使った不正なフローを防ぐ。
どうしても外部向けにOAuth機能を見せる場合は、ギガサイト便のような会社ドメイン認証付き共有URLを発行し、特定の組織のメールアドレスを持つ人だけがアクセスできる設定にする。これにより、不特定多数がHTMLにアクセスして認可フローを試みるリスクを大きく減らせる。
OAuthの動作を見せることが目的の場合、実際のOAuthを使わずにモック認証(固定のテストユーザーで即時ログイン成功にする)として実装したHTMLを共有する方が安全だ。UIの確認はモックで十分なケースが多く、実際のOAuthが必要になるのは本番環境に近い受け入れテストの段階だ。
- HTML内の外部script・form action・iframeを確認する
- APIキーやトークンらしき文字列がないか検索する
- 画像・CSS・JSのパスが公開後も解決できるか見る
- noindexと認証を混同せず、必要なら両方設定する
再発防止ルール
AI生成のHTMLでOAuth実装が含まれる場合、プロジェクトのコーディング規約に「client_secretはフロントエンドに記述しない」「callback処理はサーバーサイドのエンドポイントで行う」という二つのルールを追加する。これをPRのチェックリストに組み込むと、レビュー時の見落としを防げる。
使い終わったテスト用OAuthアプリは必ずコンソールから削除する手順を定める。共有URLの有効期限が切れた日と同じタイミングでOAuthアプリを削除するカレンダーリマインダーを設定しておくと、削除を忘れたまま放置するケースを防げる。
OAuth関連の事故を防ぐために、四半期に一度OAuthアプリの一覧をProviderコンソールで確認し、現在も必要なアプリと不要なアプリを仕分けるレビューを行う。長期間使われていないアプリは削除し、使用中のアプリはredirect_uriの設定が最小限になっているかを確認する。
- HTML内の外部script・form action・iframeを確認する
- APIキーやトークンらしき文字列がないか検索する
- 画像・CSS・JSのパスが公開後も解決できるか見る
- noindexと認証を混同せず、必要なら両方設定する
よくある質問
OAuth Providerがredirect_uriを厳密に検証しているなら、callback URLがHTMLに書かれていても問題ないのではないですか?
ProviderのURL検証はフローの整合性を守るが、HTMLに書かれたclient_idとcallback URLの組み合わせを見た攻撃者がフィッシングページを作る材料にすることは防げない。情報を公開しないこと自体が防御の一層になる。
テスト用OAuthアプリを本番と分ける場合、GitHubのシークレットやvercelの環境変数で管理するのが良いですか?
はい。client_idとclient_secretはリポジトリのシークレットや環境変数として管理し、HTMLに直書きしない設計が基本だ。ローカル開発では.envファイルに書きgitignoreに追加し、CI/CDでは環境変数として注入する構成が標準的だ。
共有URLの有効期限が切れた後も、OAuthアプリ自体は削除しなくてよいですか?
共有URLが失効してもOAuthアプリは残り続けるため、攻撃者がclient_idを使って認可リクエストを送ることは引き続き可能だ。使い終わったテスト用アプリはURLの失効と同時に削除することを推奨する。