先日タイトルの問題にぶつかったので対応方法などやったことを雑に書く。

環境


  • PHP7.3
  • Laravel 5.5
  • AWS CloudFront / ELB / EC2

現象や設定


  • POST時に 常にTokenMismatchException が常に発生する
  • ローカルで動かしたときは全く問題なかった
  • Cookieを確認するとXSRF-TOKENが保存されていない
    • .envの設定値もローカルと同一
  • セッション保存先はredisにしている
    • fileに設定変更して試した場合、POSTするたびにstorage/framework/sessionsにセッションファイルが作成される

原因と対応


この例外は結構みんなはまっているようで、私と同じようにローカルの開発環境では問題ないけどサーバに載せるとダメな人もいた。原因はいろいろあるようだが自分の場合はAWS CloudFrontの設定でCookieがEC2に送信されないようになっていたのが原因だった。

AWS Cloudfront causing CSRF Token Mismatch Exception

従ってCloudFrontでCookieを転送するように設定するだけで解決した。BehavirorsからDefault(*)のもののForward Cookiesを編集すればよい。

AllとWhiteListが選択可能。

AWS 公式ドキュメント

他に試したやつ


せっかくなので他に試したものも書く。いろいろ調べたり試したりが、調べた内容のほとんどは下記の質問に対する回答で網羅されている。つまり、この問題が発生する原因はそれくらいまばらともいえる。(ただし、下記には先に言及しているAWS CloudFrontについての回答は存在しない)

Laravel 5.3 - TokenMismatchException in VerifyCsrfToken.php line 68:

これらの対応で解決した人もいるようなので、参考になるかもしれないしやったことを雑に書いておく。

Cookie削除したらなおった説

そもそも今回はXSRF-TOKENがCookieにセットすらされていないので関係なかったが、一番初めにやってみた対応ではある。

CSRF-TOKENを出力してみる

これは対応方法というよりは調査のために行ったことだが、laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.phptokensMatchで比較しているトークンをvar_dumpで出力した。これをやってトークンが毎回異なることを確認した。

sessionの設定がおかしい説

.envSESSION_DOMAINに正しいドメインを設定しないといけない説。AWSにのせた後はサブドメインで動かしており、Laravel側でサブドメインでない方のドメインのCookieと混ざって[1]問題が発生しているのかと疑ったが問題なかった。

bladeにcsrf_tokenを書いてない説

bladeにcsrf_token()を書いてないという説。当然書いてたし、値もちゃんと生成されてた。

sessionストレージのパーミッション説

セッションの保存方法がfileの場合にstorage/framework/sessionsのパーミッションが悪いという説。そもそもredisでやってたし、保存方法をfileに変えた場合にも前述の通りPOSTするたびにセッションファイルが生成されるだけだった。

PHPタグの前にスペースがあるよ説

どうも特定のファイル(session.phpなど???)の<?phpタグの前にスペースやタブがあるとレスポンスにCookieがセットされないらしい。\s+<\?phpで検索できる。念のためタブも調べたい場合は\t+<\?phpでも検索できる。VSCodeの正規表現検索で調べたが、開発しているソースコードではこれも問題なかった。

感想


CookieにXSRF-TOKENセットされていないのにはすぐ気付いたが、そこからがめちゃくちゃ時間かかって一日が爆散した。ソフトウェア開発はつらい。


  1. 似たような問題が4系の時にあったと思う ↩︎