実のところ、ウェブサービスのセッションつーものがどうやって実現されているのかをあまり理解してなかった。 特にRuby on Railsではセッションの内容自体をサーバのDBじゃなくてクッキーに保存するということらしいが、その場合のセキュリティ的なこともあまり自信がなかった。 のでちょっと調べてみた。
先にまとめ
- Railsではセッションの内容がクッキーでやりとりされる(デフォルトの場合)
- Rails6 ではクッキーが暗号化されているので、クライアント側で改ざんはできない
- クッキーには
httpOnlyが指定されているので、ブラウザによってサーバに送られるだけでJavaScriptから読み出されることはない
Railsでのセッションの使用方法
例えばユーザにログインさせる場合に、なんらかの認証をした後にコントローラでセッションにユーザIDをセット:
class SessionsController < ApplicationController |
すると以降コントローラで session[:user_id] が nil じゃなければログイン済みとして扱う、などとする。
ここで疑問だったのは、
- クライアントでクッキーを書き換えられたら他人になりすましてログインされてしまうのでは?
- クッキーを盗まれたらまずいんじゃ?
というあたりが不思議だった。
セッションクッキーの内容とクライアント側での書き換え
Railsではセッションクッキーには値を生のままではなく暗号化して送るようになっているので、秘密鍵を知らない限り復元も再暗号化もできないはず。
なのでクライアント側で内容を書き換えるということはできないので、
直接 user_id を書き換えることで他人としてログインできてしまう、ということはない。
ただし内容自体をクッキーでやり取りしているので、クライアント側でのクッキーの削除による情報消失や、 クッキー内容をコピーしておいて後で上書きして過去の状態に巻き戻るというのはあり得る (CookieStoreセッションに対する再生攻撃)
クッキーの盗み見・漏洩
まずクッキーが盗まれたらまずいんじゃ?というのはまったくその通り。 とはいえクッキーストアじゃなくてセッションIDだけを送る方式でも、盗まれたらセッションが奪われてしまってまずいのは同じ。 なので盗まれないようにすることが重要。
https 経由であれば、通信経路で内容を盗み見られても内容はわからないはず。
ウェブサイトでJavaScriptを使用している場合にうかつなモジュールを組み込んだことで、 またはXSSによって埋め込まれたコードでクッキーを盗み取られるようなことはないのか。
Railsのセッションクッキーは httpOnly となっていてJavaScriptからアクセスできないので、スクリプト実行による漏洩はおきない。
セッションクッキーの内容の確認
Google Chromeではメニューから検証>Application>Cookies 内でクッキーが確認できる。
_myapp_session という名前が使われている。
セッションクッキーから内容を復元
クライアント側では改ざん不能といってもサーバ側では扱える必要があるので、復元する方法があるはず。
Decrypt Rails 6.0 beta session cookies
# `bundle exec ruby ...` で直接実行したい場合、以下を実行させる |
上記スクリプトをRailsアプリディレクトリ内に保存し、 rails runner でコマンドライン引数に与えてやると復元できる:
$ session_cookie='wWL47h2B...--TImX...--e8t%...' # ブラウザで確認したセッションクッキーの内容 |
saltは"authenticated encrypted cookie"(デフォルト設定のままでいいんだろうか?)encrypted_cookie_cipherはnil(なので'aes-256-gcm'が使われる)secret_key_baseは128文字の16進数文字列(=512ビット)rails new時にconfig/master.keyとcredentials.yml.encが自動生成され、その中で保持されているdevelopment時にはtmp/development_secret.txtが使われる
key_lenは32session_keyは"_myapp_session"
セッションIDの取得
サーバ側でセッションIDを取得するには、いろいろ方法がある:
session.id |
セッションクッキーの削除
ユーザがログアウトした場合に、ブラウザ側でセッションクッキーが削除されるようにする必要がある。
Railsでは session.clear ですべての項目、 session.delete で個別に削除できる。
ただし項目は削除されるが勝手に再度作成されるのか、セッションクッキー自体は送られていて、ブラウザから _myapp_session 自体が削除されるわけではない
(session にアクセスしない状態や session.clear 後でも勝手にセッションクッキーが送られている)。
セッションクッキーを送らない、というようなことはできないぽい…。
またセッションを削除してもサーバ側で管理しているわけではないので、クライアント側で過去の内容を保持しておいて復元した場合には無効とならずに復帰されてしまう。
そもそもセッションとは?
ここまできて、実のところ「セッション」という単語が指す内容の想定がずれているんじゃないかという気がしてきた…。 よくショッピングカートの例とか出てくるけど、例えばアマゾンのショッピングカートとかを想像すると、 ブラウザを閉じても別端末でアクセスしてもカートに追加した商品が保持されるが、こういうものはセッション情報には含まないのではないかという気がしてきた。
もっと一時的な内容、例えば複数画面に渡るユーザ情報登録やアンケートなど、ブラウザのページを閉じたら捨てていいような内容のことを指すのが適切な気がする。
参考
ActionDispatch::Session::CookieStore
Your cookies will be encrypted using your apps secret_key_base.
Rails アプリケーションを設定する - Railsガイド
config.action_dispatch.encrypted_cookie_cipher: 暗号化済みcookieに使う暗号化方式を設定します。デフォルトは"aes-256-gcm"です。Rails SessionにCookieStore使った時の問題点 - OAuth.jp
- セッションIDをやり取りする方式と比べて、クッキーストアは漏洩した場合にログアウトしてもパスワード変更しても失効しないのが問題。 失効日時を含めておけば一定期間で無効化できるが、自前でチェックする必要がある。