メール列挙保護を有効または無効にする

このガイドでは、メール列挙保護機能について説明し、この機能を有効または無効にする方法について説明します。2023 年 9 月 15 日以降にプロジェクトを作成した場合、メール列挙保護はデフォルトで有効になっています。

概要

メールの列挙はブルート フォース攻撃の一種です。悪意のある人物が、API にメールアドレスを渡して応答を確認することにより、システム内のユーザーの推測または確認を試みます。

メール列挙保護がないと、Identity Platform はメール列挙攻撃で悪用される可能性がある情報を返します。

  • システムに存在しないメールアドレスでログインしようとしています。Identity Platform は EMAIL_NOT_FOUND エラーを返します。

  • システムにすでに存在するメールアドレスで登録しようとしています。Identity Platform は EMAIL_EXISTS エラーを返します。

Identity Platform のメール列挙保護機能を使用すると、アプリのユーザー アカウントをこれらの攻撃から保護できます。

メール列挙保護が有効になっている場合、プロジェクトは次のように動作します。

  • fetchSignInForEmail API が失敗します。SDK バージョン 22.3.0(Android)または 10.18.0(iOS)または 10.6.0(ウェブ)より前のバージョンでは、匿名の認証済みユーザーをメールアドレスとリンクさせることはできません。

  • createAuthUri REST API またはすべてのプラットフォームで fetchSignInMethodsForEmail クライアント SDK メソッドを呼び出す際に、指定したメールアドレスのログイン方法の一覧は返されなくなります。

  • ユーザーは、新しいアドレスを最初に確認しないと、メールアドレスを変更できません。たとえば、以下の方法でメールアドレスを変更することができなくなります。update REST API、setAccountInfo REST API、またはすべてのプラットフォームでの updateEmail クライアント SDK メソッド。

    代わりに、ウェブと Android の場合は verifyBeforeUpdateEmail、iOS の場合は sendEmailVerification(beforeUpdatingEmail:) を使用できます。

  • setAccountInfo REST API を使用してメール / パスワード プロバイダを既存のユーザー アカウントにリンクできなくなります。linkWithCredential クライアント SDK メソッドと EmailAuthCredential は、どのプラットフォームでも使用できなくなります。代わりに REST API signUp を使用して、idToken フィールドと、emailpassword フィールドのユーザーの ID トークンを渡してリンクします。

  • sendOobCode REST API をリクエスト タイプ VERIFY_AND_CHANGE_EMAIL または PASSWORD_RESET で呼び出すことで開始されるメール認証フローのエラー レスポンス、および、ウェブと Android では verifyBeforeUpdateEmail、iOS では sendEmailVerification(beforeUpdatingEmail:)、すべてのプラットフォームで sendPasswordResetEmail クライアント SDK メソッドを呼び出すことで開始されるメール認証フローのエラー レスポンスを削除します。

    パスワードの再設定をリクエストした場合、確認メールはメールアドレスが存在する場合にのみ送信されます。メールアドレスの変更をリクエストした場合、確認メールはメールアドレスがまだ存在しない場合のみ送信されます。どちらの場合も、メールが送信されなかったことを示す特定のエラー メッセージはありません。

    メール確認フローなしではユーザーが登録できないようにすることをおすすめします。

  • 無効なログインの場合は、INVALID_LOGIN_CREDENTIALS エラー レスポンスが返されます。無効な登録ケースでは、引き続き EMAIL_EXISTS エラーが返されます。次のセクションの推奨事項をご覧ください。

アプリがメール列挙保護によって変更された動作に依存している場合は、現在、それを無効にできます。ただし、これは長期的には推奨されません。次のセクションをご覧ください。

セキュリティに関する推奨事項

アカウント乗っ取り攻撃を実行する最も一般的な方法の 1 つは、漏洩した認証情報や推測しやすい認証情報を使用して認証情報の不正使用攻撃を実行することです。メール列挙は、ユーザーに関する機密情報を取得することや、ユーザーに対してフィッシング攻撃を仕掛けることにも使用できます。このような理由から、Google ではこのような攻撃からアプリを保護するために、メールの列挙保護機能を使用することをおすすめしています。

  • 2023 年 9 月 15 日以降にプロジェクトを作成した場合、メール列挙保護はデフォルトで有効になっています。メール列挙保護を有効にしたままにして、このガイドの前半で説明した動作に依存しないことをおすすめします。

  • 2023 年 9 月 15 日より前にプロジェクトを作成した場合、メール列挙保護は自動的に有効になりません。

    アプリがこのガイドの前半で説明した動作に依存していない場合は、メール列挙保護を直ちに有効にすることをおすすめします。

    アプリが前述の動作に依存している場合は、できるだけ早く移行を開始し、メール列挙保護を有効にすることをおすすめします。

メール列挙保護を使用するだけでなく、EMAIL_EXISTS エラーを返し続けているプロジェクトの登録エンドポイントの不正使用を防ぐ対策を検討してください。方法は次のとおりです。

  • App Check を有効にする
  • reCAPTCHA に登録フローに統合する。
  • メールアドレスとパスワードによるログインやメールリンクによるログインを禁止し、Google ログインなどの別の方法を使用する。

メール列挙保護を有効にする

メール列挙保護を有効にする手順は次のとおりです。

Firebase コンソール

  1. Firebase コンソールで、[Firebase Auth Settings] ページに移動します。

    Firebase Authentication の設定に移動

    1. ナビゲーション パネルで [ユーザー アクション] を選択します。

    2. [メール列挙保護(推奨)] を選択します。

  2. [保存] をクリックします。

Node.js

  1. Admin SDK をインストールします。

  2. メール列挙保護を有効にするには、次のいずれかを使用します。

    • プロジェクト レベルでの保護:

      import { getAuth } from 'firebase-admin/auth';
      
      getAuth().projectConfigManager().updateProjectConfig(
        {
          emailPrivacyConfig: {
            enableImprovedEmailPrivacy: true,
          },
        }
      );
      
    • テナントレベルでの保護:

      import { getAuth } from 'firebase-admin/auth';
      
      getAuth().tenantConfigManager().updateTenant(TENANT_ID,
        {
          emailPrivacyConfig: {
            enableImprovedEmailPrivacy: true,
          },
        }
      );
      

      TENANT_ID は、メール列挙保護を有効にするテナント ID に置き換えます。

REST

  1. Google Cloud コンソールで、gcloud auth print-access-token コマンドを使用してプロジェクト ID のアクセス トークンを出力します。

    gcloud auth print-access-token --project=PROJECT_ID
    
  2. Identity Toolkit API を使用して、プロジェクト ID のメール列挙保護を有効にします。

    curl -X PATCH -d "{'emailPrivacyConfig':{'enableImprovedEmailPrivacy':true}}" \
        -H 'Authorization: Bearer ACCESS_TOKEN' \
        -H 'Content-Type: application/json' -H 'X-Goog-User-Project: PROJECT_ID' \
        "https://identitytoolkit.googleapis.com/admin/v2/projects/PROJECT_ID/config?updateMask=emailPrivacyConfig"
    

以下を置き換えます。

  • ACCESS_TOKEN: 先に生成したアクセス トークン。
  • PROJECT_ID: プロジェクト ID

メール列挙保護を無効にする

メール列挙保護を有効にする手順は次のとおりです。

Firebase コンソール

  1. Firebase コンソールで、[Firebase Auth Settings] ページに移動します。

    Firebase Authentication の設定に移動

    1. ナビゲーション パネルで [ユーザー アクション] を選択します。

    2. [メールの列挙保護(推奨)] をオフにします。

  2. [保存] をクリックします。

Node.js

  1. Admin SDK をインストールします。

  2. メール列挙保護を無効にするには、次のいずれかを使用します。

    • プロジェクト レベルでの保護:

      import { getAuth } from 'firebase-admin/auth';
      
      getAuth().projectConfigManager().updateProjectConfig(
        {
          emailPrivacyConfig: {
            enableImprovedEmailPrivacy: false,
          },
        }
      );
      
    • テナントレベルでの保護:

      import { getAuth } from 'firebase-admin/auth';
      
      getAuth().tenantConfigManager().updateTenant(TENANT_ID,
        {
          emailPrivacyConfig: {
            enableImprovedEmailPrivacy: false,
          },
        }
      );
      

      TENANT_ID は、メール列挙保護を無効にするテナント ID に置き換えます。

REST

  1. Google Cloud コンソールで、gcloud auth print-access-token コマンドを使用してプロジェクト ID のアクセス トークンを出力します。

    gcloud auth print-access-token --project=PROJECT_ID
    
  2. Identity Toolkit API を使用してメール列挙保護を無効にします。

    curl -X PATCH -d "{'emailPrivacyConfig':{'enableImprovedEmailPrivacy':false}}" \
        -H 'Authorization: Bearer ACCESS_TOKEN' \
        -H 'Content-Type: application/json' -H 'X-Goog-User-Project: PROJECT_ID' \
        "https://identitytoolkit.googleapis.com/admin/v2/projects/PROJECT_ID/config?updateMask=emailPrivacyConfig"
    

以下を置き換えます。

  • ACCESS_TOKEN: 先に生成したアクセス トークン。
  • PROJECT_ID: プロジェクト ID

エラー レスポンスの例

ユーザーが間違ったメールアドレスまたはパスワードでログインしようとした場合、またはシステムにすでに存在するメールアドレスで登録しようとした場合、Identity Platform は次のようなエラーを返します。

{
"code": "auth/internal-error",
"message": "{\"error\":{\"code\":400,\"message\":\"INVALID_LOGIN_CREDENTIALS\",\"errors\":[{\"message\":\"INVALID_LOGIN_CREDENTIALS\",\"domain\":\"global\",\"reason\":\"invalid\"}]}}"
}