🍣

【Identity Platform 活用】⑤ Identity Platform を使う上で抑えておきたいセキュリティ知識

2025/02/04に公開

1. はじめに

こんにちは、クラウドエース第三開発部の秋庭です。
前回の記事では、Identity Platform のテナント機能でできることについてご紹介しました。
https://zenn.dev/cloud_ace/articles/253178c575ded7
今回は Identity Platform を使用する上でのセキュリティに関する情報について、ご紹介できればと思います。

以下の点については今回の記事で取り扱いませんので、ご了承ください。

2. ID トークン

ID トークンの基礎

ID トークンは、ログインした際に Identity Platform から発行される JWT です。
リクエストに ID トークンを含め、サーバーサイドで検証することでリクエストを認証することができます。

有効期限

ID トークンの期限は 1 時間に設定されています。ログイン時に同時に発行されるリフレッシュトークンを使用して、新しい ID トークンを要求できます。

Web SDK(フロントエンド)では getIdToken() を利用して ID トークンを取得すると、期限が切れている場合に自動で更新を行ってくれます。

リフレッシュトークンは基本的に有効期限がありませんが、revokeToken API を使用して無効化できます。

参考
Method: accounts.revokeToken  |  Identity Platform Documentation  |  Google Cloud
・Go Admin SDK: RevokeRefreshTokens - firebase.google.com/go/v4/auth - Go Packages
・Node.js Admin SDK: BaseAuth.revokeRefreshTokens()  |  Firebase Admin SDK
ユーザー セッションの管理  |  Firebase Authentication


ID トークンは JWT であるため、窃取された場合、有効期限が切れるまで認証が有効であることに注意してください。
ステートフルな認証を行いたい場合、Identity Platform では前述したリフレッシュトークンの取り消しと、リフレッシュトークン取り消しの検出を組み合わせることで実現することができます。

詳細は弊社唐津の記事をご確認ください
https://zenn.dev/cloud_ace/articles/identity-platform-multitenancy-session-management

保存場所

発行された ID トークンやリフレッシュトークンの保存先は、以下の 3 つから指定が可能です。

保存先 説明
localStorage or IndexedDB ブラウザやタブを閉じて、再度開いても状態が維持されます。状態を削除するには、明示的にログアウトの操作を行う必要性があるます。
sessionStorage タブおよびセッションで状態が維持され、タブやウィンドウを閉じると状態はクリアされます。
in-memory メモリ上で状態が維持され、リローディングなど画面の更新があると状態がクリアされます。

保存先は、Web SDK(フロントエンド)にて以下のように setPersistence() の箇所で指定が可能です。

import {
  getAuth,
  setPersistence,
  signInWithEmailAndPassword,
  browserSessionPersistence,
} from "firebase/auth";

const auth = getAuth();
// browserLocalPersistence or indexedDBLocalPersistence
// or browserSessionPersistence or inMemoryPersistence
setPersistence(auth, browserSessionPersistence)
  .then(() => {
    // Existing and future Auth states are now persisted in the current
    // session only. Closing the window would clear any existing state even
    // if a user forgets to sign out.
    // ...
    // New sign-in will be persisted with session persistence.
    return signInWithEmailAndPassword(auth, email, password);
  })
  .catch((error) => {
    // Handle Errors here.
    const errorCode = error.code;
    const errorMessage = error.message;
  });

公共のスペースでの利用が想定される場合、認証の情報が永続的に保存されるとセキュリティリスクが上がるため、localStorage よりも sessionStorage の方が適していると言えます。
一方、多要素認証がユーザーに推奨されている場合、sessionStorage を指定していると誤ってタブを閉じた際に多要素認証でのログインのやり直しが発生してしまうため、体験が悪くなってしまう可能性があります。
社内など安全な場所での利用が想定される場合、localStorage が適した選択肢となりそうです。
アプリケーションがどのように使われるかを想定して選択するのがよいと思います。

参考
認証状態の永続性  |  Firebase

CSRF XSS 対策

リクエストのヘッダーに ID トークンを付与する場合と、Cookie を付与する場合のセキュリティの違いについて確認します。

session cookie の作成方法

ログインをすることで ID トークン(JWT)が発行されることは既に解説しましたが、Admin SDK(サーバーサイド)or REST API では ID トークンを使用することで session cookie を作成することができます。

参考
セッション Cookie を管理する  |  Firebase Authentication
Method: projects.createSessionCookie  |  Identity Platform Documentation  |  Google Cloud
・Go Admin SDK: SessionCookie - firebase.google.com/go/v4/auth - Go Packages
・Node.js Admin SDK: createSessionCookie()  |  Firebase Admin SDK

項目 ID トークン(JWT) Session Cookie
保存場所 localStorage / IndexedDB / sessionStorage / in-memory Cookie Store
送信方法 クライアントが Authorization ヘッダーに含める ブラウザが自動送信
CSRF 対策 Authorization ヘッダーを使うと CSRF を防げる SameSite 属性 で防止
XSS 耐性 XSS に弱い(盗まれる可能性あり) HttpOnly 属性なら XSS 耐性が少し高い(送信されることを防げる訳ではない)
有効期限 1 時間 5 分 ~ 14 日(作成時に指定)
ステートレス/ステートフル ステートレス(ステートフルに変更可能) ステートレス(ステートフルに変更可能)

項目について詳しく見ていきます。

送信方法では、以下のような違いがあります。

  • ID トークン: リクエスト毎にヘッダーに付与
  • session cookie: リクエスト毎に付与する必要性がない(Cookie の失効毎にバックエンド側で生成して付与)

CSRF に対しては、ID トークンの場合も session cookie の場合も同等であると考えて良さそうです。

XSS に対しては、ID トークンは中身を見られた上で不正なリクエストも送られてしまうと言えます。
HttpOnly 属性を付与した session cookie の場合、cookie の内容を見られることはありませんが、不正なリクエストに認証情報が付与されてしまうという点では同等と言えます。

有効期限は、発行される ID トークンは 1 時間固定となっていますが、session cookie は有効期限を作成時に指定することが可能です。

ステートレスかステートフル化に関しては #有効期限 の項で説明したように、リフレッシュトークンの無効化を検出することでステートフルを実現することが可能となっています。

大きな差はないので、セキュリティ要件に応じて ID トークンか session cookie かを選択するのが良いと思います。

3. アカウントロック

Identity Platform のユーザーには有効/無効、メールアドレス確認済/未確認などの状態がありますが、その他に、ログインに一定数失敗するとどのような操作も受け付けなくなる一時的なアカウントロックのような状態が存在します。

アカウントロックの状態の時にリクエストを送信すると、以下のようなレスポンスが返されます。
アカウントロック状態になると、正しいパスワードを送っても同様のレスポンスが返されるようになってしまいます。

"TOO_MANY_ATTEMPTS_TRY_LATER : Access to this account has been temporarily disabled due to many failed login attempts. You can immediately restore it by resetting your password or you can try again later."

レスポンスメッセージにあるように、この状態になった場合はパスワードをリセットすることでその場でアカウントロック状態が解除され、操作が行えるようになります。

辞書攻撃などから守るためにこのような動作になっていますが、ユーザーがログインのミスで同様のレスポンスを返されることもあると思うので、開発者側はこの仕様を知っておくと良いと思います。

アカウントロックの仕様に関して、2025 年 1 月現在公式ドキュメントに詳しい記載はありません。
2024 年 4 月頃に、挙動について私が動作検証した際の表を以下に記載します。
試行の間隔が短いほど早くアカウントロック状態になり、ログインに失敗し続けるとアカウントロックの時間が伸びていくようです。

比較表
リクエスト回数 経過秒数 送信したパスワード レスポンス内容 ロックされている時間
1 0 誤(ったパスワード) INVALID_PASSWORD
2 15 INVALID_PASSWORD
3 30 INVALID_PASSWORD
4 45 INVALID_PASSWORD
5 60 INVALID_PASSWORD
6 75 TOO_MANY_ATTEMPTS_TRY_LATER ~ 約 30s
7 90 INVALID_PASSWORD
8 105 TOO_MANY_ATTEMPTS_TRY_LATER
9 120 TOO_MANY_ATTEMPTS_TRY_LATER 約 15s ~ 45s
10 135 INVALID_PASSWORD
11 150 TOO_MANY_ATTEMPTS_TRY_LATER
12 165 TOO_MANY_ATTEMPTS_TRY_LATER
13 180 TOO_MANY_ATTEMPTS_TRY_LATER
14 195 TOO_MANY_ATTEMPTS_TRY_LATER 約 45s ~ 75s
15 210 INVALID_PASSWORD
16 225 TOO_MANY_ATTEMPTS_TRY_LATER
17 240 TOO_MANY_ATTEMPTS_TRY_LATER
18 255 TOO_MANY_ATTEMPTS_TRY_LATER
19 270 TOO_MANY_ATTEMPTS_TRY_LATER
20 285 TOO_MANY_ATTEMPTS_TRY_LATER
21 300 TOO_MANY_ATTEMPTS_TRY_LATER
22 315 TOO_MANY_ATTEMPTS_TRY_LATER
23 330 TOO_MANY_ATTEMPTS_TRY_LATER
24 345 TOO_MANY_ATTEMPTS_TRY_LATER
25 360 TOO_MANY_ATTEMPTS_TRY_LATER
26 375 TOO_MANY_ATTEMPTS_TRY_LATER
27 390 TOO_MANY_ATTEMPTS_TRY_LATER
28 405 TOO_MANY_ATTEMPTS_TRY_LATER 約 180s ~ 210s
29 420 INVALID_PASSWORD
30 435 TOO_MANY_ATTEMPTS_TRY_LATER
リクエスト回数 経過秒数 送信したパスワード レスポンス内容 ロックされている時間
1 0 INVALID_PASSWORD
2 60 INVALID_PASSWORD
3 120 INVALID_PASSWORD
4 180 INVALID_PASSWORD
5 240 INVALID_PASSWORD
6 300 INVALID_PASSWORD
7 360 INVALID_PASSWORD
8 420 TOO_MANY_ATTEMPTS_TRY_LATER ~ 120s
9 480 INVALID_PASSWORD
10 540 TOO_MANY_ATTEMPTS_TRY_LATER
11 600 TOO_MANY_ATTEMPTS_TRY_LATER 60s ~ 180s
12 660 INVALID_PASSWORD
13 720 TOO_MANY_ATTEMPTS_TRY_LATER
14 780 TOO_MANY_ATTEMPTS_TRY_LATER
15 840 TOO_MANY_ATTEMPTS_TRY_LATER
16 900 TOO_MANY_ATTEMPTS_TRY_LATER
17 960 TOO_MANY_ATTEMPTS_TRY_LATER
18 1020 TOO_MANY_ATTEMPTS_TRY_LATER 300s ~ 420s
19 1080 INVALID_PASSWORD
20 1140 INVALID_PASSWORD
リクエスト回数 経過秒数 送信したパスワード レスポンス内容 ロックされている時間
1 0 INVALID_PASSWORD
2 10 INVALID_PASSWORD
3 20 INVALID_PASSWORD
4 30 INVALID_PASSWORD
5 40 INVALID_PASSWORD
6 50 TOO_MANY_ATTEMPTS_TRY_LATER
7 60 TOO_MANY_ATTEMPTS_TRY_LATER
8 70 TOO_MANY_ATTEMPTS_TRY_LATER
9 80 TOO_MANY_ATTEMPTS_TRY_LATER
10 90 TOO_MANY_ATTEMPTS_TRY_LATER 40s ~ 60s
11 100 INVALID_PASSWORD
12 110 TOO_MANY_ATTEMPTS_TRY_LATER
13 120 TOO_MANY_ATTEMPTS_TRY_LATER
14 130 TOO_MANY_ATTEMPTS_TRY_LATER
15 140 TOO_MANY_ATTEMPTS_TRY_LATER
16 150 TOO_MANY_ATTEMPTS_TRY_LATER
17 160 TOO_MANY_ATTEMPTS_TRY_LATER
18 170 TOO_MANY_ATTEMPTS_TRY_LATER 60s ~ 80s
19 180 INVALID_PASSWORD
20 190 TOO_MANY_ATTEMPTS_TRY_LATER

4. メールアドレスの列挙保護

Identity Platform は、メールアドレスの列挙保護機能により、ブルートフォース攻撃対策を提供しています。
無効の場合、ログイン時にメールアドレスが間違っていた際、EMAIL_NOT_FOUND が返されますが、攻撃者はレスポンスからメールアドレスが使用されているかどうかを推測できます。
列挙保護が有効になっている場合、INVALID_LOGIN_CREDENTIALS が返されるようになり、レスポンスからメールアドレスが使用されているか判別できないようになります。
2025 年 1 月現在、この機能はプロジェクト作成時にデフォルトで有効になっています。

参考
メール列挙保護を有効または無効にする  |  Identity Platform Documentation  |  Google Cloud

5. データの保持リージョン

2025 年 1 月現在、Firebase Authentication および Identity Platform では、データの保存リージョンを選択できません。
保存先はアメリカ国内のリージョンに固定となるため、日本国内にデータを保管する必要がある場合は注意が必要です。

参考
データを保管する場所と処理する場所

6. API キーの権限

Firebase Authentication および Identity Platform の利用を開始した際に API 実行に必要な API キーが作成されます。

API キーは例として、Web SDK(フロントエンド)で以下のように初期化に使用されます。

const firebaseConfig = {
  apiKey: "AIza******",
  authDomain: "****.firebaseapp.com",
};
const app = initializeApp(firebaseConfig);

Identity Platform のみを使用する場合、初期状態では API キーに過剰な権限が付与されていることがあります。

呼び出せる API が制限されていない状態

Identity Platform のみに使う API キーであれば、以下の API を選択すれば動作します。

  • Identity Toolkit API
  • Token Service API

必要に応じて、API キーにサービスに必要な API を追加してください。

フロントエンドで初期化に使用されていることから分かる通り、この API キーは露出しています。
意図しない API へのアクセスを防ぐため、事前に API キーの権限を絞っておくことをお勧めします。

参考
・弊社記事: Firebase の API キーは公開しても大丈夫?

7. テナントアクセスのコントロール

IAM を利用して、テナントの情報にアクセスできるプリンシパルを制限することができます。

詳細は前回の記事をご参照ください。
https://zenn.dev/cloud_ace/articles/253178c575ded7#アクセス制御

8. ログ

Identity Platform では、ユーザーのアクティビティを「アクティビティロギング」で記録できます。
管理者の操作ログは「監査ログ」を使用して記録することができます。

参考
アクティビティ ロギング  |  Identity Platform Documentation  |  Google Cloud
Identity Platform の監査ロギング  |  Identity Platform Documentation  |  Google Cloud

9. 準拠しているセキュリティ規格

Firebase Authentication および Identity Platform は以下のセキリティ規格に準拠しています。

  • ISO 27001
  • ISO 27017
  • ISO 27018

参考
ISO と SOC への準拠

10. おわりに

Identity Platform を使用する上で知っておくとよいセキュリティ関連の知識について、今回の記事でご紹介しました。

この記事が、IDaaS 導入の際の検討の一助になれば幸いです。
ここまで読んで頂き、ありがとうございました。

Identity Platform 関連記事

https://zenn.dev/cloud_ace/articles/6d8d1502d3c90f
https://zenn.dev/cloud_ace/articles/9b47b0dab6d3ab
https://zenn.dev/cloud_ace/articles/c65abf3ca87336
https://zenn.dev/cloud_ace/articles/253178c575ded7

Discussion