- 投稿日:2020-11-13T18:16:18+09:00
Laravelのemailバリデーション
Laravelデフォルトのバリデーションルール「email」を使用したWEBサービスを作成したところ、バリデーション不足を指摘された。
環境
Laravel 5.8.38問題点
ユーザー登録画面にて、
test@example.comあいうえお
といった、@以降にひらがなを含むメールアドレスを登録できてしまう。RegisterController.phpprotected function validator(array $data) { return Validator::make($data, [ 'name' => ['required', 'string', 'max:100'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 'password' => ['required', new AlphaNumHalf, 'min:8', 'confirmed'], ]); }実際に登録されたメールアドレス
test@example.xn--com-m63bkmoq
まじか・・・
結論
'email'
↓
'email:strict,dns,spoof'
とりあえずこれ。dnsによりドメインが存在するアドレスのみバリデーションを通るので、意味不明な日本語を入れたらまず通らないようにはできると思う。
が、本当にこれが正しいのか分からないので、色々調べてみる。何が起きているのか
Laravelの公式ドキュメントによれば、
フィールドがメールアドレスとして正しいことをバリデートします。内部でこのバリデーションルールはメールアドレスの検証にegulias/email-validatorパッケージを使用しています。デフォルトではRFCValidationバリデータが適用されますが、他のバリデーションスタイルも適用可能です。
適用可能なバリデーションスタイルは、次の通りです。
・rfc: RFCValidation
・strict: NoRFCWarningsValidation
・dns: DNSCheckValidation
・spoof: SpoofCheckValidation
・filter: FilterEmailValidationemailバリデーションにも種類があるらしい。今更知った。デフォルトではRFCValidationが適用されるとのこと。
それぞれの詳細は下記サイトが参考になった。
【Laravel 5.8.33】メールアドレスの新バリデーション・実例全5件!では、他のバリデーションスタイルで
test@example.comあいうえお
を登録しようとするとどうなるか。結果はこちら。
バリデーションスタイル 結果 rfc test@example.xn--com-m63bkmoq
strict test@example.xn--com-m63bkmoq
dns 登録できない spoof test@example.xn--com-m63bkmoq
filter test@example.xn--com-m63bkmoq
それぞれのバリデーションの詳細をみるとそりゃそうだよな、という感じ。
xn--com-m63bkmoqとはなんなのか
調べてみると、Punycodeという符号化方式で、
comあいうえお
の部分がアルファベット、数字、ハイフンのみの文字列xn--com-m63bkmoq
に変換されているようだ。ドメイン名として Punycode を使用する際は、ピリオド(.)で区切られたドメイン名の階層レベルごとにプレフィックスとして「xn--」を使用し、エンコードされた文字列を続ける。大文字と小文字は区別されない。
可読なドメイン名 Punycodeでのドメイン名 ドメイン名例.jp xn--eckwd4c7cu47r2wf.jp ウィキペディア.ドメイン名例.jp xn--cckbak0byl6e.xn--eckwd4c7cu47r2wf.jp 可愛いね.そうでもないよ xn--n8j5d625jn9k.xn--n8jd2ewbp7lub 引用:Punycode
可愛いね.そうでもないよは笑う。
また、試しに日本語JPドメイン名のPunycode変換・逆変換にて
comあいうえお
と入力してみると、やはりxn--com-m63bkmoq
に変換された。カスタムバリデーションで正規表現を使ってみる
正規表現を使ったカスタムバリデーションを試そうともしたが、Punycodeでアルファベット、数字、ハイフンのみの文字列に変換されてしまうのならば、RFCValidation等と同じ結果になってしまうのではないだろうか?
念のため試してみる。
emailの正規表現は難しいらしく、どれを使えば良いのか・・・という感じだが、こちらの記事を参考にさせていただいた。
メールアドレスを表す現実的な正規表現自作のバリデーションルール
MyEmailRegex.phppublic function passes($attribute, $value) { return preg_match("/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/", $value); }RegisterController.phpuse App\Rules\AlphaNumHalf; protected function validator(array $data) { return Validator::make($data, [ 'name' => ['required', 'string', 'max:100'], 'email' => ['required', 'string', new MyEmailRegex, 'max:255', 'unique:users'], 'password' => ['required', new AlphaNumHalf, 'min:8', 'confirmed'], ]); }この正規表現の意味は、「何かしらの文字@英数字か"-".英数字か"-"」となる。
全角文字がPunycodeでアルファベット、数字、ハイフンのみの文字列に変換されてからバリデーションを通っていそうだが、そうであればやっぱり正規表現で引っ掛けるのは難しいのではないか。
test@example.comあいうえお
を登録してみると、やっぱりバリデーションに引っかからずtest@example.xn--com-m63bkmoq
と登録された。このようなバリデーションは無難であると先の記事には書いてあるが、それなら僕の受けた指摘は一体なんなのだと釈然としない。
strict,dns,spoofを併用する
Laravelが用意してくれているバリデーション
- strict: NoRFCWarningsValidation
- dns: DNSCheckValidation
- spoof: SpoofCheckValidation
は、併用が可能。
RegisterController.phpprotected function validator(array $data) { return Validator::make($data, [ 'name' => ['required', 'string', 'max:100'], 'email' => ['required', 'string', 'email:strict,dns,spoof', 'max:255', 'unique:users'], 'password' => ['required', new AlphaNumHalf, 'min:8', 'confirmed'], ]); }なぜ併用するかというと、
strict
だけだと先に述べたようにtest@example.comあいうえお
が通ってしまう。
dns
だけだと.test..@gmail.com
等が通ってしまう。
※RFCでは@の前に.をつけること等が禁止されている。
strict
でRFCに違反するアドレスがはじかれ、
dns
でドメインが有効でないアドレスがはじかれる。
ついでにspoof
でなりすましメールもはじかれる。ドメイン部分に日本語を入力し、Punycodeで変換されたものが実際に有効なドメインでなければ(そんなドメインがあるのか分からないが)、日本語を入力してもしっかりバリデーションしてくれるようになった。
結論
Laravelのemailのバリデーションについて、デフォルトのまま
email(email:rfc)
では不十分な場合がある。その場合は、email:dns
等、他のバリデーションも併用すると良さそう。ただ、バリデーションの種類云々と同じくらい大事なのが、「どの程度のバリデーションでサービスの依頼主は満足するか?」を、質問なり打ち合わせでしっかり確認しておくことだと感じた。(僕は何も確認せずに納品してしまったので)
記事の内容等に間違いがありましたら、申し訳ございません。
おまけ
vendor/laravel/framework/src/Illuminate/Validation/Concerns/ValidatesAttributes.phppublic function validateEmail($attribute, $value, $parameters) { if (! is_string($value) && ! (is_object($value) && method_exists($value, '__toString'))) { return false; } $validations = collect($parameters) ->unique() ->map(function ($validation) { if ($validation === 'rfc') { return new RFCValidation(); } elseif ($validation === 'strict') { return new NoRFCWarningsValidation(); } elseif ($validation === 'dns') { return new DNSCheckValidation(); } elseif ($validation === 'spoof') { return new SpoofCheckValidation(); } elseif ($validation === 'filter') { return new FilterEmailValidation(); } }) ->values() ->all() ?: [new RFCValidation()]; return (new EmailValidator)->isValid($value, new MultipleValidationWithAnd($validations)); }vendor/laravel/framework/src/Illuminate/Validation/Concerns/FilterEmailValidation.phpnamespace Illuminate\Validation\Concerns; use Egulias\EmailValidator\EmailLexer; use Egulias\EmailValidator\Warning\Warning; use Egulias\EmailValidator\Exception\InvalidEmail; use Egulias\EmailValidator\Validation\EmailValidation; class FilterEmailValidation implements EmailValidation { /** * Returns true if the given email is valid. * * @param string $email * @param EmailLexer * @return bool */ public function isValid($email, EmailLexer $emailLexer) { return filter_var($email, FILTER_VALIDATE_EMAIL) !== false; } /** * Returns the validation error. * * @return InvalidEmail|null */ public function getError() { // } /** * Returns the validation warnings. * * @return Warning[] */ public function getWarnings() { return []; } }参考サイト
・emailのバリデーションについて
PHPで各種バリデーション
Gmailが日本語など非アルファベット文字を含むメールアドレスとの送受信に対応
RFC 5322 & 5321に沿ったメールアドレス(local-part)に使える文字まとめ
メールアドレスを表す現実的な正規表現
バリデーションで使用する正規表現の読み方・ドメインについて
ドメイン名のしくみ・Laravelバリデーションについて
Laravel 5.8 バリデーション
【Laravel 5.8.33】メールアドレスの新バリデーション・実例全5件!
バリデーションエラー/POST送信時のLaravelの挙動を追う・Punycodeについて
Punycode
日本語JPドメイン名のPunycode変換・逆変換
- 投稿日:2020-11-13T17:48:52+09:00
PHPのキャスト演算子
キャスト演算子とは
キャスト演算子は右辺オペランドのデータ型を指定した型に変換する。
(ここで登場するオペランド
とは演算子の対象となる値のこと。)以下の例では
シングルクォート
で囲んでいる '10' は文字列型
として扱われる。
文字列型をキャスト演算子 (int) で整数型へ変換する。<?php $a = '10'; $b = (int) $a; echo gettype($a)."<br/>\n"; echo gettype($b)."<br/>\n"; ?>結果は以下のようになります。 $a = 文字列 $b = 整数キャスト演算子の種類
サンプルコード
<?php $var = 3.14; $var = (string)$var; echo gettype($var)."<br/>\n"; $var = (int)$var; echo gettype($var)."<br/>\n"; $var = (bool)$var; echo gettype($var)."<br/>\n"; $var = (float)$var; echo gettype($var)."<br/>\n"; $var = (array)$var; echo gettype($var)."<br/>\n"; $var = (object)$var; echo gettype($var)."<br/>\n"; $var = (unset)$var; echo gettype($var)."<br/>\n"; ?>結果はこのようになります。 string integer boolean double array object NULL引用元
こちらの記事を参考に作成しました
- 投稿日:2020-11-13T05:29:13+09:00
docker 上の composer 経由で指定位置に laravel プロジェクトを用意するメモ
結論: どうやって実現するか
※ docker は事前にインストール・起動済みとします。(コダワリがなければ Docker Desktop https://www.docker.com/products/docker-desktop や WSL2 上に Docker をインストールして使えるようにしとけば OK です。)
例えば、
~/Documents/tmp/laravel-project-via-composer
という laravel プロジェクトを作るなら以下。% cd ~/Documents/tmp % docker run --rm -i -t -v $PWD:/app composer create-project --prefer-dist laravel/laravel laravel-project-via-composer
- laravel プロジェクトを配置したいディレクトリに移動して
- そのディレクトリで
composer
のオフィシャルイメージを使って、 laravel プロジェクトを準備するこれだけ。
(なんなら、
-i -t
も不要だが、出力が見やすくなるので入れてる。)これで
~/Documents/tmp/laravel-project-via-composer
に用意される。
$PWD
を任意のディレクトリすれば、コマンドの発行元ディレクトリはどこでも良いので、% cd ~/Documents/tmp
も要らない。(が、うっかりミスとか怖いから、個人的にはcd
してからコマンド実行したい派。)念の為に書くと
$PWD:/app
の右辺が/app
となっているのは https://github.com/composer/docker/blob/fbef2df32932859094b4cd3fb072f1e93a7fa1f8/2.0/Dockerfile#L60 にて、WORKDIR /app
となっているから。※つまり、もし、何らかの理由で将来 composer の Docker image の WORKDIR の位置が変わったら同様にコマンド発行時のパスも変更することになる。(使う image の Dockerfile を読むの大切。)
なぜこの方法を使うのか
- ローカルの環境の PHP や Composer のバージョンとか気にしたくない
- anyenv とかでも管理できるけど、 OpenSSL のバージョン齟齬とかで面倒…
- build するより docker image 使ったほうが早い
- 多くの場合、本来的目的は、 build をしたいのではなく、 laravel プロジェクトを用意したいだけ
- laravel プロジェクト用意した後に、 WEB App 実行用の Dockerfile や docker-compose 用ファイルは別途用意したら良い
たぶん、他のある程度有名な FW やパッケージマネージャーがあるなら同様の考え方で WEB App の下準備ができるはず。(ハードウェアとの通信が必要とか、そういうのは別のお話。)
ハードウェアトラブルや、ハードウェアの乗り換えとかで、いちいちローカル環境を整備するコストを考えると馬鹿らしいので、使えるものは使うと便利かも知れませんよ?というお話でした。
ちなみに
laravel/installer
をインストールしてlaravel new hoge
という選択肢も当然にあるのですが、 docker 使って開発環境用意するだけなら composer の image 使って実行した方が楽やん?って思ってます。知らんけど。