正しいメールアドレスのチェック方法がちょっとした話題になっているようです。Web屋のネタ帳でも取り上げられていますが、メールアドレスのチェック方法自体は解説していません。ついでなので書いておきます。
「本当に正しいメールアドレスかチェック」するには実際にメールを送信して、送信されたユーザしか知り得ない情報をユーザが知っている事により確認しなければなりません。これはWeb屋のネタ帳で解説されている通りです。
安全でより確実なメールアドレスのチェック方法
きちんと正規表現でメールアドレスをチェックするのは面倒です。しかも、RFCを守らない大手企業もあり、正規表現でチェックするのは諦めるのが妥当でしょう。
記入されたメールアドレスが正しいかチェックする手順
- @でスプリット(分割)する
- 配列要素数が2つかチェック。NGはエラー
- 1つ目の要素(ユーザ名部分)および2つ目の要素(ドメイン部分)に空白や制御文字、非ASCII文字が含まれていないかチェック。NGはエラー
- 2つ目の要素(ドメイン部分)がDNSのMXレコードを持つかチェック。NGはエラー
これらのチェックに通過したメールアドレスはOKとしています。
これならおかしなアドレスを使っている某社の事等も考えずに済みます。MXレコードを持つサーバに限定すると、DNS設定を正しく行っていないメールサーバは受け入れなくなります。必要ならDNSが引けるかどうかだけのチェックにしておいても構いません。
参考:
正規表現を使ったチェックにはDoSリスクがあります。問題のある正規表現の場合、DoS攻撃に脆弱になる場合があります。
備考
RFC的にはAレコードがあればメール送受信に支障は無いです。管理者の設定ミス/経験不足などでMXレコードが無いケースも考えられます。ダイナミックDNSを利用したメールサーバもあります。しかし、普通のメールアドレスのドメイン部分はMXレコードを持っています。ダイナミックDNSでメールサーバ運用する事はお勧めできる運用形態ではありませんが、MXレコード無し運用されているサーバがあるのも事実です。こういったサーバのメールアドレスも許可したい場合はMXでなくA/CNAMEレコードが引けるかチェックします。ただし、「ダイナミックIPのメールサーバを許可する=クライアントに自前メールサーバを許可する」事になります。これでは「正しいメールアドレスか確認する」目的が達成できない点に注意してください。(だれでも認証の時だけ使えるアドレスを用意できる)
PHPのmail, mb_send_mail関数は関数レベルでメールヘッダインジェクション対策が施されているので、CR,LFが混じっていてもインジェクション攻撃に脆弱になりません。しかし、他の環境ではメールヘッダインジェクションが可能になる場合があります。最低限、CR、LFだけはチェックし、含まれる場合はセキュリティ上のエラーとして処理(攻撃が行われたとアラートを発生させる等)しなければなりません。
後はメールを送信して認証するだけです。
- 認証URLを含むメールを送信
- 認証URLがリクエストされる
- 認証用の鍵が一致するかチェック。不一致はNG
ところで、メールアドレス認証等に使う鍵は十分なエントロピーを持ったデータのハッシュ値(SHA1など)を利用するようにします。UNIXなら/dev/urandomが良いと思いますが、urandomでなくてもPHPのmt_rand関数+uniqid関数の出力等でも必要なエントロピーを持っている考えて大丈夫でしょう。(追記:WindowsではOpenSSLモジュールが無いと/dev/urandom相当のエントロピーソースが無い。新しいPHPでより良いソースがサポートされた場合、そちらを使うべきです)
$token = sha1('SOME_RANDOM_PREFIX' . uniqid(mt_rand(), true));
最後に、迷惑メール防止のため一定時間は同じメールアドレスに対して認証用のメールを繰り返し送信しないような仕様にする事を忘れない様にします。
追記
比較的最近のRFC仕様のメールアドレスを「エスケープ無し」でHTMLに出力するとJavaScriptインジェクションが、SQLに出力するとSQLインジェクションができる、と話題になっているようです。以下のエントリに書いた事を認識されている開発者なら問題になるようなコードは書かないハズです。
- エンジニア必須の概念 – 契約による設計と信頼境界線
- 入力バリデーションはセキュリティ対策として*あてにする*ものではありません
- APIとエスケープ/バリデーションとセキュリティ
- Webアプリセキュリティの基本ルール