こんにちは。セキュリティの世界へようこそ。
現場でバリバリコードを書いているエンジニアの皆さん、あるいは「セキュリティって何から手をつければいいの?」と悩んでいるIT担当者の皆さん。今日は、セキュリティの「入り口」でありながら、意外と見落とされがちな「ログイン画面のエラーメッセージ」という小さな穴についてお話しします。
ここを少し工夫するだけで、あなたのサービスは格段に「泥棒が嫌がる家」になりますよ。
—
泥棒は「家の中」に入る前に、まず「表札」を確認する
想像してみてください。あなたは今、泥棒の視点に立っています。狙うのは、セキュリティがガバガバな家ではなく、入りやすそうな家です。
泥棒は、いきなり鍵を壊すようなリスクの高いことはしません。まずは、「この家にはどんな人が住んでいるのか?」という表札を確認します。
- 「あ、この家は〇〇さん(会員)がいるな」
- 「この家はパスワードを間違えても教えてくれる優しい家だな」
ログイン画面で、ユーザーがIDやパスワードを間違えた時に「ユーザーが存在しません」や「パスワードが違います」と律儀に教えてしまうシステム。これは、泥棒に対して「このIDは実在するから、あとはパスワードだけ総当たりで試せば入れるぞ」と親切に教えているようなものなんです。
これが、セキュリティ業界で言う「ユーザー列挙攻撃(User Enumeration)」の入り口です。
—
「優しさ」が「脆弱性」になる瞬間
開発者としては、ユーザーがログインに失敗した時に「IDが違うのか、パスワードが違うのか」を教えてあげたほうが親切ですよね。ユーザーも「あ、IDを打ち間違えたかな?」とすぐに気づけます。
しかし、セキュリティの現場では、「親切=攻撃者へのヒント」になり得ます。
攻撃者はツールを使って、何万というIDリストをあなたのログイン画面に流し込みます。その際、エラーメッセージの違いを監視するだけで、「実在するユーザーIDのリスト」を瞬時に作り上げてしまうのです。一度リストが完成すれば、あとはそのIDに対してパスワードの総当たり(ブルートフォース)を仕掛けるだけ。
だからこそ、私たちは「誰であろうと、同じ返答をする」というルールを徹底します。
—
「曖昧さ」こそが最強の防犯対策
対策はシンプルです。「IDとパスワードのどちらが間違っているか、決して判別させない」こと。
ログイン失敗時に出すメッセージは、以下のように統一しましょう。
❌ ダメな例:
- 「ユーザー名が見つかりません」
- 「パスワードが一致しません」
✅ 推奨される例:
- 「メールアドレスまたはパスワードが正しくありません」
これなら、攻撃者は「IDが存在するのか、パスワードが違うのか」を判断できません。これが、泥棒に「この家は表札を隠しているから、入るのが面倒くさそうだ」と思わせる防犯対策なのです。
—
実装で気をつける「もう一つの罠:レスポンス時間」
実は、メッセージを統一しただけではまだ安心できません。プロの攻撃者は、「レスポンスが返ってくるまでの時間(ミリ秒単位の差)」まで見ています。
例えば、プログラムの処理順序がこうなっているとします。
1. DBを検索する(ユーザーがいるか確認)
2. ユーザーがいればパスワードを照合する
この場合、ユーザーが存在する場合としない場合で、処理にかかる時間に微妙な差が出ます。「ユーザーが存在する時の方が、パスワード照合の分だけ0.01秒遅いな」と気づかれてしまうのです。
実装のヒント(コード例)
これを防ぐには、ユーザーがいようがいまいが、一定の処理を通す工夫が必要です。
// 簡易的な擬似コードの例です
async function login(username, password) {
// 1. ユーザーを検索(ダミーユーザーを用意しておく考え方もあります)
const user = await findUserByUsername(username);
// 2. ユーザーが存在しなくても、存在する場合と同じ時間をかけるために
// 意図的に偽のパスワードハッシュと比較を行う(定数時間比較)
const hashToCompare = user ? user.passwordHash : dummyHash;
const isValid = await verifyPassword(password, hashToCompare);
// 3. メッセージは常に同じにする
if (user && isValid) {
return “ログイン成功”;
} else {
// どちらが間違っていても、同じメッセージを返す
return “IDまたはパスワードが正しくありません”;
}
}
※ 本番環境では、定数時間で比較するライブラリ(crypto.timingSafeEqualなど)を使うのが鉄則です。
—
最後に:セキュリティは「完璧」を目指さなくていい
セキュリティに完璧はありません。ですが、「攻撃のコスト」を上げることなら誰でもできます。
ログイン画面を少しだけ不親切にする。たったそれだけで、無差別な攻撃のターゲットから外れることができます。セキュリティとは、難しい呪文を唱えることではなく、「どうすれば相手が諦めてくれるか」を想像する知恵のことなんです。
まずは明日、あなたのサービスのログイン画面を確認してみてください。「優しすぎるエラーメッセージ」が出ていませんか?
一歩ずつ、泥棒が嫌がる強いシステムを作っていきましょう。また次回の記事でお会いしましょう!
コメント