[解説] Better AuthのXSS脆弱性 (GHSA-86j7-9j95-vpqj) とフロントエンドでの対策
はじめに
日本のフロントエンドエンジニアの皆さん、こんにちは。今回は、認証・認可ライブラリ`Better Auth`に発見された深刻度Highの脆弱性GHSA-86j7-9j95-vpqjについて解説します。この脆弱性はStored DOM XSSに分類され、認可サーバーのオリジンで任意のJavaScriptが実行され、アカウントの乗っ取りにつながる可能性があります。特に`oidc-provider`または`mcp`プラグインを利用しているプロジェクトは注意が必要です。
脆弱性の概要
`Better Auth`のバージョン`1.6.12`以前(stable版)または`1.7.0-beta`の全ビルド(pre-release版)に存在する`oidc-provider`および`mcp`プラグインにおいて、OAuthクライアント登録時に`redirect_uris`のスキーマ検証が不十分であったことが原因です。これにより、攻撃者は`javascript:`スキームを含む悪意のあるリダイレクトURIを登録できてしまいます。
ユーザーがそのクライアントを介して認証フローを進め、同意ページで承認すると、攻撃者が仕込んだ`javascript:`スキームのリダイレクトURIがブラウザのナビゲーションターゲット(例: `window.location.href`)に割り当てられ、認可サーバーのオリジンで悪意のあるJavaScriptが実行されます。これにより、セッション情報の窃取やアカウント乗っ取りが発生するリスクがあります。
技術的詳細:なぜXSSが発生するのか?
この脆弱性の核心は、OAuth 2.0のクライアント登録エンドポイントが`redirect_uris`の値を適切に検証していなかった点にあります。通常、リダイレクトURIは`http:`や`https:`のような安全なスキームを持つべきですが、脆弱なバージョンでは`javascript:alert('XSS')`のようなスキームも登録できてしまいました。
攻撃者は、悪意のある`javascript:` URIを`redirect_uri`として登録したクライアントを用意します。そして、被害者をこのクライアントを用いた認可フローに誘導します。被害者が同意画面で認可を承認すると、認可サーバーは同意結果として、登録された悪意のある`redirectURI`をクライアントコンポーネント(フロントエンド)に返します。
問題となるのは、このフロントエンドの同意ページが、サーバーから受け取った`redirectURI`を検証せずに`window.location.href`や`location.assign`などのブラウザのナビゲーションターゲットにそのまま代入してしまう場合です。ブラウザは`javascript:`スキームのURLをナビゲーションターゲットとして受け取ると、その後の文字列を現在のオリジンでJavaScriptコードとして実行します。これにより、認可サーバーのオリジン上で攻撃者のスクリプトが実行され、セッション情報へのアクセスやAPI呼び出しが可能になるというメカニズムです。
あなたは影響を受けるか?フロントエンドエンジニアのためのチェックリスト
以下の条件をすべて満たす場合、あなたのアプリケーションは影響を受ける可能性があります。
1. あなたのアプリケーションが`better-auth/plugins`の`oidc-provider`プラグイン、または`mcp`プラグインを有効にしている。
2. あなたの`better-auth`のバージョンが`1.6.12`以前(stable版)、または`1.7.0-beta`のいずれかのビルド(pre-release版)である。
3. あなたのアプリケーションの同意ページが、`authClient.oauth2.consent(...)`の応答から`redirectURI`を読み取り、それを`window.location.href`、`location.assign`、`location.replace`などのブラウザのナビゲーションターゲットに割り当てている。
#### 影響を受けないケース
以下のいずれかの条件を満たす場合、あなたのアプリケーションは影響を受けません。
1. プロジェクトの`consent-buttons.tsx`デモをそのまま使用している場合。
2. Next.jsやRemixのサーバーアクションなど、サーバーサイドのリダイレクトで同意フローを完了している場合。ブラウザは`Location`レスポンスヘッダーで提供された`javascript:`URLを追従しません。
3. `@better-auth/oauth-provider`を使用している場合。このパッケージは既に`redirect_uris`を検証しています。
対策と回避策
### 1. アップグレード(推奨)
最も推奨される対策は、`better-auth`を修正済みのバージョンにアップグレードすることです。これにより、`oidc-provider`および`mcp`プラグインの`redirect_uris`検証が強化されます。
- Stable版の場合: `better-auth@1.6.13`へアップグレード
- Pre-release版の場合: `1.7.0-beta.4`へアップグレード
### 2. フロントエンドでの回避策:同意ページの強化
すぐにアップグレードできない場合、フロントエンドの同意ページで、返された`redirectURI`をナビゲーションに使う前に、そのスキームを検証してください。`http:`または`https:`スキームのみを許可し、それ以外は拒否してエラーを表示するなどの処理を実装します。
例: const navigateToRedirectUri = (uri: string) => { try { const url = new URL(uri); if (url.protocol === 'http:' || url.protocol === 'https:') { window.location.href = uri; } else { // 不正なスキームの場合、エラーを表示するか別の処理を行う console.error('不正なリダイレクトURIスキーム:', url.protocol); // 例: エラーメッセージを表示 alert('安全でないリダイレクトURIが検出されました。'); } } catch (e) { console.error('リダイレクトURIのパースに失敗:', e); // 例: エラーメッセージを表示 alert('リダイレクトURIの形式が不正です。'); } }; // サーバーからの応答データが res.data.redirectURI に格納されていると仮定 // navigateToRedirectUri(res.data.redirectURI);
この回避策は、サーバーが不正なURIを返しても、フロントエンドでシンク(実行ポイント)を閉じるため効果的です。
### 3. `@better-auth/oauth-provider`への移行
`oidc-provider`および`mcp`プラグインは非推奨となっており、`@better-auth/oauth-provider`が後継とされています。この後継パッケージは既に`redirect_uris`の安全な検証を実装しているため、長期的な解決策として移行を検討してください。
### 4. クライアント登録の制限
もし`allowDynamicClientRegistration`が`true`に設定されている場合、これをデフォルトの`false`に戻し、認証されたユーザーのみがクライアント登録できるように制限してください。これは脆弱性を根本的に解決するものではありませんが、未認証の攻撃者からのリスクを低減します。
まとめ
今回は`Better Auth`の`oidc-provider`および`mcp`プラグインにおけるStored DOM XSS脆弱性について、その詳細とフロントエンドエンジニアが取るべき具体的な対策を解説しました。認証・認可関連の脆弱性は、アプリケーション全体のセキュリティに深刻な影響を与える可能性があります。
常に利用しているライブラリのセキュリティ情報を確認し、迅速なアップデートを心がけましょう。また、フロントエンド側でもユーザー入力(今回のケースではサーバーから受け取った値ですが、その元は攻撃者による入力)の検証を徹底することで、多層的なセキュリティを構築することが重要です。
本記事が皆さんのアプリケーションのセキュリティ向上の一助となれば幸いです。