タイトルにだいぶ語弊があるけど、的確な表現が思いつかなかった…。
Railsは全く縁がなくて基礎知識ゼロだったのですが、今後の自分の役に立ちそうだったので備忘録として残しておきます。
前提
あるRuby on Rails 4製のWebアプリがあり、ユーザは[page A]->[form B]の順に遷移します。
このWebアプリを作ったのは自分と全然関係ない人たちなので、ソースコードや設定値等を見ることはできませんし、詳細な仕様もわかりません。
form BへのリクエストをWebブラウザ以外のRESTクライアントで行いたいのですが、form Bへのリクエストにはpage Aで発行されたcsrf_tokenと、(このアプリはcookie sessionを採用しているため)cookieが必要です。
知りたいこと
一度page Aにアクセスしてしまえば、以降はcsrf_tokenとcookieを使い続けることで、form Bに直接リクエストを投げることができます。
ただ、今後似たような場面に遭遇した場合、できれば無駄なアクセスを無くしたいので 「page Aに一度もアクセスせずにtokenを発行する方法はないのか?」 が気になりました。
調べたこと
tokenは自分で発行できるか?
Rails セキュリティガイド - Rails ガイドや、RailsでのCSRF token 発行 / 検証のロジック - Qiitaを参照すると、「セッションに保存された_csrf_tokenの値」と「HTMLページから取得したcsrf-tokenの値」で検証を行って、問題がなければリクエストが通る仕組みのようです。
それなら、検証を通過するような値を自分で作ってcookieに書き込めば理屈としては問題ない気がします。果たしてそんなことはできるのか?
Railsのcookieの扱いはどうなっているのか?
page Aで発行されたcookieの中身を見てみると以下のような感じでした。
#LWP-Cookies-1.0
Set-Cookie3: アプリ名_session=dmJ3aTYvbG8yRE91K2Mxa3dRU016TWNNUnZicTVXSWFud2VSMzRIQVMrQ3V4enBtUkRPTHg3UnFnbFRGSGV4eWduVmgwdTEzRGRjZFJEOV(中略)klpbTREczJiSnI2UExPV2ZQZmpVTEM3UURKLS1CWWlsOVJLVnFEVkE0QndpanhOczlnPT0%3D--daa857c08b96dc69cf56bd4f12706fd3b7b2920c; path="/"; domain=xxx.xxx.xxx; path_spec; secure; discard; HttpOnly; version=0
Base64エンコードされてるっぽい値なので、「デコード後に値を書き換えてエンコードし直せばいけるのでは?」と思ったのですが、さすがにちゃんと対策されていました。ですよね。
以下のページに答えが載っていました。
Cookies on Rails | BigBinary Blog
session , by default, uses signed cookies which prevents any kind of tampering of data but the data is still visible to users.
値の中(--
以降)にハッシュ値が存在していて、改ざんすると正確なハッシュ値が計算できないので、アプリがnilを返すんですね。以下のページもわかりやすかったです。
Rails で (Rack の) セッション情報を Cookie に保存する仕組み - Qiita
Rails 4 stores session data in encrypted format
加えて、Rails 4ではユーザがMarshal.loadで中身を覗けないようにしてありました。実際試しましたが、以下のようにエラーが発生しました。
convert.rb:13:in `load': incompatible marshal file format (can't be read) (TypeError)
format version 4.8 required; 110.56 given