Railsアプリケーションに対して、外からPOST送信しようとすると、422エラー・Can't verify CSRF token authenticityエラーが出ます。
これはRailsが自動で生成してくれるCSRF対策によるものらしく、よくわかっていなかったのでまとめてみました。
CSRFとは
CSRF(クロスサイトリクエストフォージェリ)は攻撃の一種なのですが、簡単に説明すると「攻撃者がターゲットに、自分で自分を攻撃させる攻撃」です。
もうちょっと詳しく言うと、「自分がログインしている(常時ログインなどで)サービスへ、知らぬ間にリクエストを送らされる攻撃」です。
通常、常時ログインなどでは、cookieにセッション情報が格納されますが、Railsではリクエストのたびにcookieを自動的に取得します。
そして、これこそがCSRFがつけいるセキュリティの穴なんです。
どういうことかというと、cookieはブラウザもしくはサーバに保存されているので、別サイトからリクエストが送られたときにも正しいcookieが使用されてしまう(認証が通ってしまう)のです。
画像にコマンドを仕込むなどすれば、リクエストを知らぬ間に送らせることは簡単なので、ユーザーが意図しないリクエストが通ってしまうことになります。
CSRF対策の定番
なので、Webアプリ側でそのような攻撃を防ぐ必要があるのですが、そのための方法として、アプリ内でリクエストをする際は認証用のtokenを差し込むというものがあります。
つまり、「お前味方か?」という確認をするということです。そしてその結果として必然的に、外からのリクエストをすべて弾くことになります。
RailsのCSRF対策
Railsではありがたいことに、そのCSRF対策を自動生成してくれています。
それがapplication_controllerが作られるときに自動で書き込まれるprotect_from_forgeryメソッドです。
protect_from_forgery メソッド
実は、RailsではViewでform_forやform_tagを使うと、自動でCSRF対策用のトークンを埋め込んでくれています。
そして、protect_from_forgeryによって、アクション実行前に正しいトークンが付随されているかをチェックしています。
逆に言えばこのメソッドの適用外は自分で対策をしなければならないということでもあります。
ー 参考
Rails セキュリティガイド ー 3.クロスサイトリクエストフォージェリ (CSRF)
Ajax送信を行うとき
じゃあ、AjaxでPOST送信したいときは、form_forなどを使ってないので自分で対策しなくてはいけないかというと、Railsはここまでカバーしてくれています。
Ajaxの場合、Ajax送信する直前に実行されるメソッドで、トークンを指定しているようです。
ー 参考
RailsのCSRF対策の仕組みについて
外部からの送信を受けたいとき
このメソッドはCSRF対策しているから大丈夫、しなくても大丈夫、だから外部からリクエストできるようにしたい。という場合はこんな回避方法があるようです。
- protect_from_forgery with: :null_session
コントローラーごとしなくてOKの場合は、こうすると良いようです。APIを作るとき用のものみたいですね。
ー 参考
Rails 4.0だとCSRFトークンでエラーになる - protect_from_forgery :except => [:action]
そのメソッドだけというときはexceptで指定すると良いようです。
ー 参考
Rails4のCSRF対策で「Can't verify CSRF token authenticity」エラー