enjoy Railsway!! 第2回 セッションストレージの脆弱性対応

Railsでアプリケーションを構築する際、公開前に脆弱性検査用のツールや専門の第三者によって脆弱性がないか検査すると安全です。
デフォルト状態のRailsアプリケーションでは、残念ながらこの検査によりいくつかの脆弱性が発見されることになります。
診断結果への対応は大変骨が折れる作業になることが多く、また実施フェーズもスケジュールの後ろの方になりがちなため、可能な限り早い段階で対応しておきたいものです。

今回はこういった脆弱性のうち、セッションストレージに着目した対応の一つをご紹介します。

※このコラムのプログラム等は以下の環境で動作確認しています
(2023/06/30最終確認)
 ruby: ruby 3.2.2 [arm64-darwin22]
 rails: Rails 7.0.5.1
 activerecord-session_store: 2.0.0

目次

セッションストレージ

Railsではセッション変数を容易に扱える仕組みとして「セッションストレージ」が用意されており、デフォルトではCookieStore(ActionDispatch::Session::CookieStore)が利用されます。
これはセッション変数を含めたセッション情報をクライアントのCookieとして暗号化された状態で保存します。

「暗号化されているから安心!」と思いきや、実際は以下のような問題を抱えています。

問題: サーバー側でセッションを無効化できない

セッションを初期化、無効化する目的でreset_sessionを実行したとします。
サーバー側では新しいセッション情報を発行し、既存のCookieを上書きするようなレスポンスを返します。
ところが、クライアント側で上書きされる前のCookieを確保しておくと、その時点のセッションを復元できてしまいます。
正しい形式で暗号化されたCookie情報をクライアントから受信した場合、サーバー側ではそのセッションが無効化されたものかどうか判別することができません。
この挙動は、例えばセッション変数にログイン情報を保存している場合に問題となります。
サーバー側で当該ユーザーの全セッションを無効化したつもりができていなかった、という状況が起こり得ます。

対策: セッションストレージを切り替える

この問題に対応するには、セッション情報をサーバー側で保持する必要があります。
サーバー側で保持する、ということは一般的にデーターベースを利用することになりますが、これを自前で実装してしまうと煩雑になってしまいます。
そこで利用したい機能が、セッションストレージとして指定できるGem,activerecord-session_storeです。

activerecord-session_store

このGemを導入し、セッションストレージとして:active_record_store (ActionDispatch::Session::ActiveRecordStore)を指定すると、これまでクライアントのCookieに保存されていたセッション変数がActiveRecordを通してデータベースに保存されるようになります。

導入方法

GitHubの記載を参考にすると、簡単に導入することができます。

Gemfileに記述を追加し、bundle installします。

Gemfile

gem 'activerecord-session_store' # 追記

コマンドを実行し、マイグレーションファイルを生成します。

bin/rails g activerecord:session_migration

コマンドを実行し、マイグレーションを行います。

bin/rails db:migrate

続いてsession_storeを指定します。
これはconfig/application.rbconfig/initializers/session_store.rbなどで指定できます。
keyexpire_afterは適切に設定してください。

config/application.rb

module YourApp
  class Application < Rails:: Application
    # ~~~
    config.session_store :active_record_store, key: '_your_app_session', expire_after: 14.days
  end
 end

または、

config/initializers/session_store.rb

Rails.application.config.session_store :active_record_store, key: '_your_app_session', expire_after: 14.days

以上でセッションストレージの切り替えは完了です。
以降、クライアントのCookieにはセッションIDのみが保存され、セッション情報そのものはサーバー側のデータベースに保存されるようになります。
サーバー側でセッション情報を無効化すると、仮にクライアント側から無効化される前のCookie情報が送られてきたとしても、不正な値として無視することができます。
これにより、セッション情報をサーバー側でコントロールできるようになります。

無効になったセッション情報を削除する

なお、このままですと有効期限を迎えたセッション情報がデータベースに残り続けてしまいます。
セッション情報が保存されているテーブル、sessionsをきれいにするためのrakeコマンドが用意されていますので、適宜利用します。

  • bin/rake db:sessions:clear
    • セッション情報がすべて削除されます。開発環境下で便利です
      • ただし、TRUNCATE TABLEを実行するためsqliteでは利用できません…
  • bin/rake db:sessions:trim
    • 一定期間更新されていないセッション情報が削除されます。
      • デフォルトは30日で、SESSION_DAYS_TRIM_THRESHOLD環境変数で指定することができます
        • SESSION_DAYS_TRIM_THRESHOLD=0 bin/rake db:sessions:trim とすることですべて削除できます。こちらはsqliteでも利用できます

本番環境ではcrontabなどでスケジュール設定して実行することが適切でしょう。
Gem wheneverをおすすめします。

config/schedule.rb

# 毎日3時に古いセッション情報を削除する
every 1.day, at: '3:00' do
  rake 'db:sessions:trim'
end

まとめ

Railsアプリケーションのセッションストレージの脆弱性対応として、activerecord-session_storeを利用する方法をご紹介しました。
CookieStoreは扱いやすく便利な仕組みですが、セキュリティ観点では決して堅牢とはいえません。
アプリケーション開発の最初に、その他のセッションストアを設定してしまった方が安全です。
毎回の作業になりますので、私は前回ご紹介したboilerplateに含めています。

なお、これだけではまだセッション周りの脆弱性が残っていることがあります。
Rails セキュリティガイドを参照の上、脆弱性を作り込むことが無いようくれぐれも注意して開発に取り組みましょう。

著者/文責: 泉 隼人
・Rails技術者認定試験運営委員会 テクニカルアドバイザー
・鹿児島県 喜界島出身。10歳の頃N88-BASICに触り、コンピューティングにのめり込む
・興味の赴くまま様々なプラットフォーム、言語を楽しみつつ10数年来に渡りRuby on Railsでの開発業務に従事する
・Webサイト https://9uelle.jp/

お知らせ

当委員会ではRails試験を全国300か所で一年中実施をしています。興味がある方は以下をご覧の上、是非受験ください。https://railsce.com/exam

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次