セキュリティを担保したつもりが
有効なメールアドレスかどうかを検証するために以下のような流れで処理を組むことはよくあると思います。
- メールアドレス入力(ユーザー)
- ランダムな文字列を入力されたメールアドレスに送信(システム)
- 受け取ったランダムな文字列を入力(ユーザー)
- ユーザーが入力した文字列を送信した文字列と一致するか判定(システム)
- 4の検証が正常に通過したら1で入力されたメールアドレスを有効なアドレスとして登録
そうですよね。メールアドレスの検証は絶対必要ですよね。
上記の流れに間違いはないはずです。
レビューを貰うまで完璧な実装をしたと思い込んでいました。
何が良くないのか?
問題になったのは、1で実施するユーザーが入力したメールアドレスの検証です。
ユーザーが入力したメールアドレスの検証では、以下の検証を実施しなければいけません。
- メールアドレス形式か
- DBに登録されていないメールアドレスか
- 必要であればその他バリデーション
僕が実装した内容で問題となったのは、2のDBに登録されていないメールアドレスです。
僕の実装内容では、DBに登録されているメールアドレスが入力されたら「既に登録済みのメールアドレスです」とバリデーションエラーを出力していました。
それって、バリデーションに引っかかったメールアドレスが既に使用されている誰かのメールアドレスだと世に発表してしまっている状況でした。
具体的に以下の事象を引き起こしてしまう問題です。
- バリデーションに引っかかったメールアドレスを用いて総当たり攻撃に使用される(ログインなど)
- バリデーションに引っかかったメールアドレスを自分が管理しているシステム以外で使用される
1に関しては試行回数に制限をかければ問題ないのですが、2に関しては僕らが出来ることは何一つ有りません。
だって、自分らが管理しいない場所で悪用されるからです。
ここで取得されたメールアドレスを悪意のある業者に売ったり、取得した悪意のある人がよからぬメールを送信することが出来ちゃいます。
なんて恐ろしい事態に陥ってしまうのでしょう。。
対策
対策は色々あると思いますが、僕が思いつくやつ挙げていきます。
- 「無効な値です。」というバリデーションエラーを出力
- 既に使われている事を明記しない
- 正確な対策ではない
- 認証メールに添付するエンドポイントを絶対に検証が通らないエンドポイントにする
- ランダム文字列を入力するフォームに特定の条件のみアクセス可能とするなどが必要(ちょっと面倒)
他にもあるはずなので、各自のシステムに合わせて実装してください。
預かったデータは大切に
良かれと思って実装した内容がユーザーの個人情報を危険に晒してしまうことがあることを学べた貴重な経験でした。
やりがちなミスだと思うので、皆様もお気をつけください。