LoginSignup
2
2

More than 3 years have passed since last update.

Mobile Safari は再起動時に POST リクエストする場合がある

Posted at

Rails アプリケーションで、iPhone の Safari からのリクエストでだけ ActionController::InvalidAuthenticityToken 例外が発生するので調査していたところ、Mobile Safari は再起動後、開いていたタブをロードする際に、POST リクエストで得たページであっても再度リクエストを送信しているらしいことがわかった。

※ iOS 13.5.1 の iPhone 11 で検証

検証方法

下記の Ruby スクリプトを作成して ruby server.rb -o 0.0.0.0 で起動

# gem install sinatra が必要
require "sinatra"

html = <<~HTML
<!doctype html>
<form action="/" method="post">
  <input type="text" name="email">
  <button type="submit">Submit</button>
</form>
HTML

get "/" do
  html
end

post "/" do
  p params
  html
end
  1. Mobile Safari で http://[上記スクリプト実行したPCのアドレス]:4567 にアクセス
  2. 適当に入力して submit
  3. Mobile Safari を終了
  4. Mobile Safari を起動 (確認などなしでページが表示される)

この手順でログを見ると、4 で 2 と同じリクエストが送られていることが確認できた。

x.x.x.x - - [08/Jul/2020:14:24:37 +0900] "GET / HTTP/1.1" 200 131 0.0072
{"email"=>"foo"}
x.x.x.x - - [08/Jul/2020:14:24:41 +0900] "POST / HTTP/1.1" 200 131 0.0022
{"email"=>"foo"}
x.x.x.x - - [08/Jul/2020:14:24:48 +0900] "POST / HTTP/1.1" 200 131 0.0007

Rails で InvalidAuthenticityToken 例外が起こる理由と対策

Rails で...

  1. Cookie をセッションストアとして使っていて、かつ有効期限が Session (デフォルト)
  2. protect_from_forgery with: :exception を指定している

場合に、上記のように 2 度 POST リクエストが送られると、2 度めのリクエストでは Cookie がクリアされているので session[:_csrf_token] が nil になり、params[:authenticity_token] の検証が失敗し、InvalidAuthenticityToken 例外が発生してしまう。

解決策としては下記のものが考えられる。

  1. POST でページを render せず、redirect してかならず GET させるようにする
  2. protect_from_forgery で null_session などにする
  3. Cookie の有効期限を伸ばす

3 の場合は config/initializers 下に適当なファイルを作って下記の内容を記述すればよい。

# key の部分はなんでもいいがデフォルトは "_#{アプリケーション名}_session"
# ここでは有効期限を 2 週間にしている
Rails.application.config.session_store :cookie_store, key: "_foo_session", expire_after: 2.weeks
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2