😈

【Flutter】FirebaseAuthのログイン時エラーハンドリングやりすぎてる人、危機感持った方がいい

2024/06/19に公開

🔥 導入

某インフルエンサーがいいそうなタイトルですが...
ログイン時になぜログインできないのかを丁寧に教えるのってセキュリティ的に大丈夫かな?
と思ったことがあったので記事に残そうと思いました。

🎯 やること

  • 基本的な例外処理
  • 【本題】firebase authenticationの signInWithEmailAndPassword メソッドのエラー内容によって、リターンする文言をセキュリティ考慮して制御
    ※ログイン機能導入は割愛します。

例外処理の基本

dartにおける例外処理はtry-catchです。
tryブロック内で実行された処理でエラーが発生した場合に、catchブロック内で指定した処理が実行されます。
引数eを使ってエラーを受け取れます。

ログインボタンを押した時にtry-catchする例
InkWell(
    onTap: () {
        try {
            // ログイン処理
        } catch (e) {
            // エラーメッセージを出力
            debugPrint(e);
        }
    },
    child: Text('ログイン'),
)

Firebase Authenticationの例外処理

Firebae Authentication関連のエラーは
on FirebaseAuthException catch (e)
で検出できます。
以下のように codemessage が提供されます。
codemessage の使い方は後述。

InkWell(
    onTap: () {
        try {
            await FirebaseAuth.instance.signInWithEmailAndPassword(
                email: "barry.allen@example.com",
                password: "SuperSecretPassword!"
            );
        } on FirebaseAuthException catch  (e) {
            print('Failed with error code: ${e.code}');
            print(e.message);
        }
    },
    child: Text('ログイン'),
)

参考にした公式ドキュメントはこちら

エラーコードごとにリターンする文言を変更

signInWithEmailAndPasswordメソッドで検出できるエラーコードは以下です。

  • invalid-email
    • 無効なメールアドレス形式が提供された場合に発生します。
  • user-disabled
    • 指定されたユーザーアカウントが無効化されている場合に発生します。
  • user-not-found
    • 指定されたメールアドレスに対応するユーザーが見つからない場合に発生します。
  • wrong-password
    • 提供されたパスワードが間違っている場合に発生します。
  • too-many-requests
    • 異常な数のリクエストが短時間に行われた場合に発生します。

参考にした公式ドキュメント1
参考にした公式ドキュメント2

このコードごとにリターンする文言を変えます。
View側で文言を受け取りスナックバーなどで表示すればOK!

try {
    await FirebaseAuth.instance.signInWithEmailAndPassword(
        email: email,
        password: password,
    );
} on FirebaseAuthException catch (e) {
    throw switch (e.code) {
        case 'invalid-email':
            // 「メールアドレスを正しい形式で入力してください」とリターン
            // 以降は雰囲気でお願いしますm(__)m
            return TextConstants.pageText.INVALID_EMAIL;
        case 'user-disabled':
            return TextConstants.pageText.USER_DISABLED;
        case 'user-not-found':
            return TextConstants.pageText.USER_NOT_FOUND;
        case 'wrong-password':
            return TextConstants.pageText.WRONG_PASSWORD;
        case 'too-many-requests':
            return TextConstants.pageText.TOO_MANY_REQUESTS;
        default:
            // 上記以外の場合は「予期せぬエラーが発生しました」などとリターンしておく
            return TextConstants.pageText.UNEXPECTED_ERRORS;
    };
}

【本題】これってセキュリティ的にいけてる?

やっと本題。
厳密にエラーハンドリングがなされていて、良いな!と思っていたのですが。
よくよく考えると...
エラーコードがwrong-passwordの場合「パスワードが間違っています」などの文言が表示されます。
つまり...
ログインIDとなるメールアドレスのユーザーアカウントは存在しているということを教えていることになるのでは?
これを見た不正利用者が行いそうな攻撃としては

  1. メールアドレスを固定でパスワードを複数入力する(ブルートフォース)
  2. 「あなたのアカウントが無効化されました、パスワードを正しく登録してください」などの本物そっくりのフィッシングメールを配信する

でしょうか?

🤔.。○「不正利用者怖い、情報漏洩はダメダメ」

対策

1の何度もパスワードを入力するに対しては、アカウントのロック機能を入れる などが最も有効に思えます。
が、デフォルトでFirebaseにこの機能はなさそうでした。実装するならFuncitoonsなどで制御することになりそうです。
(firebaseにアカウントロックの機能あったら教えてください🙇)
と、いうわけで比較的に簡単に実装できる方法として、
なぜログインできないのか? を不明にしてしまえ!というのが簡単にできる対策です。

改善後のエラーハンドリング

以下のように、ユーザーが見つからない場合とパスワードが間違えている場合のリターンを同じにすることで、ログインできない原因を明示しないように変更しました。

try {
    await FirebaseAuth.instance.signInWithEmailAndPassword(
        email: email,
        password: password,
    );
} on FirebaseAuthException catch (e) {
    throw switch (e.code) {
        case 'invalid-email':
            // 「メールアドレスを正しい形式で入力してください」とリターン
            // 以降は雰囲気でお願いしますm(__)m
            return TextConstants.pageText.INVALID_EMAIL;
        case 'user-disabled':
            return TextConstants.pageText.USER_DISABLED;
        // ユーザーが見つからない場合とパスワードが間違えている場合のリターンを同じにする
        case 'user-not-found':
        case 'wrong-password':
            return 'メールアドレスまたはパスワードが間違えています';
        case 'too-many-requests':
            return TextConstants.pageText.TOO_MANY_REQUESTS;
        default:
            // 上記以外の場合は「予期せぬエラーが発生しました」などとリターンしておく
            return TextConstants.pageText.UNEXPECTED_ERRORS;
    };
}

まとめ

signInWithEmailAndPassword メソッドのエラー内容を正直に表示するとセキュリティ的に危ないかもしれません。
そのため、ログインに失敗した理由はある程度ぼかした方がいいと思います。

Discussion