はじめに
Railsでログイン機能などを実装していると、cookies.signed[:user_id] や cookies.permanent といった書き方がよく登場します。
「なんとなく使っているけれど、それぞれの違いが曖昧……」という方向けに、この記事では cookies. の後に繋げる主要なメソッドとその使い分け、実戦でのセキュリティの注意点を整理しました。
対象環境
- Rails 7
よく使うもの一覧
メソッドごとの役割と使い分けを一覧表にしました。
| メソッド | ブラウザでの可読性 | 改ざん耐性 | 主な用途 |
|---|---|---|---|
cookies[:key] |
読める | 弱い | JSでも使うUIのテーマ設定など、無害なデータ |
cookies.signed[:key] |
読める | 強い(検知) | ユーザーに読まれてもいいが、改ざんは防ぎたいデータ |
cookies.encrypted[:key] |
読めない | 強い | ユーザーIDやトークンなど、絶対に隠したい機密情報 |
cookies.permanent[:key] |
(設定次第) | (設定次第) | 「ログイン状態を保持する」等の長期保存(約20年) |
cookies.signed_or_encrypted |
(設定次第) | 強い | 通常は使わない内部互換用API |
※signedは簡単には読めないが暗号化ではないという点に注意
代表的な使い方と詳細
1. 通常のCookie(平文)
最もシンプルなCookieです。ブラウザの開発者ツールから中身を簡単に見ることができ、書き換えも可能です。
cookies[:theme] = "dark_mode"
ユーザーに見られても、書き換えられてもシステムに影響がないデータにのみ使用します。
2. signed: 署名付きCookie(改ざん防止)
Railsの secret_key_base を使って署名(シグネチャ)を付与します。
暗号化はされませんが改ざん検知が可能なので、値をJSや他サービスに読ませたい際に使用します。
cookies.signed[:guest_token] = "abcde123"
ブラウザ側ではBase64形式の文字列として保存されます。
暗号化はされていないため秘匿用途には使えないということに注意です。
3. encrypted: 暗号化Cookie(秘匿と改ざん防止)
データそのものを暗号化します。ブラウザ側からは中身が一切わからなくなり、改ざんも防ぎます。
cookies.encrypted[:user_id] = user.id
ユーザーID自体は機密情報ではないため signed でも実装可能ですが、
ユーザー情報の推測やトラッキングを防ぐ目的で encrypted を使う設計も増えています。
4. permanent: 長期保存Cookie
Cookieの有効期限(Expires)を約20年後に設定します。(指定しない場合はブラウザを閉じたタイミングで破棄されます)
# 20年有効 + 暗号化 の組み合わせ(よく使います)
cookies.permanent.encrypted[:remember_token] = token
実戦で必須のセキュリティ・注意点
1. 巨大なデータは入れない(4KBの壁)
Cookieにはブラウザの仕様として、 「1つのCookieあたり 4KB まで」 という厳格な容量制限があります(1ドメインには数十個のCookieが保存可能です)。
特にsignedやencrypted は、Rails側で署名や暗号化のメタデータが付与された上でBase64エンコードされるため、元のデータよりもサイズが大きく膨らみます。
2. JSで使わないなら httponly: true をつける
Railsで単純に cookies[:key] = "value" と書いた場合、JavaScriptから読み取れる状態になります。XSS(クロスサイトスクリプティング)攻撃でCookieを盗まれないよう、JSで使わないデータには httponly: true をつけましょう。
cookies.encrypted[:secure_token] = {
value: "secret_data",
expires: 1.month.from_now, # 1ヶ月後に有効期限切れ
httponly: true, # JavaScriptからのアクセスを禁止
secure: Rails.env.production? # 本番環境(HTTPS)でのみ送信
}
RailsではセッションCookieなどは HttpOnly がデフォルトで有効ですが、JSで使う際はfalseにする必要があります。
削除系メソッド
Cookieを削除する場合は以下のメソッドを使います。
# 特定のキーを削除
cookies.delete(:user_id)
# すべてのCookieを削除(現在のドメイン)
cookies.clear
作成時に
domainやpathを指定していた場合、deleteの際にも全く同じ条件を指定しないと削除されません。(例:cookies.delete(:user_id, domain: :all))
ログイン機能(Remember Me)でのベストプラクティス
最後に、よくあるログイン保持機能の安全な実装例です。「ユーザーID」も「トークン」も、漏洩や推測を防ぐために encrypted で統一するのが現代のセオリーです。
# ログイン状態の保持
def remember(user)
# ユーザーIDが推測されるのを防ぐため encrypted を使用
cookies.permanent.encrypted[:user_id] = user.id
# トークンも漏洩を防ぐため encrypted
cookies.permanent.encrypted[:remember_token] = user.remember_token
end
# ログアウト時の処理
def forget
cookies.delete(:user_id)
cookies.delete(:remember_token)
end
まとめ
- 通常(メソッド無し): 見られても問題ないUI設定など
- signed: 改ざんは防ぎたいが、JS等で中身は読みたいもの
- encrypted: 改ざんも防ぎ、中身も絶対に隠したいもの(ユーザーIDなど)
- permanent: 長期保存(組み合わせで使う)
「ブラウザ(JS)側でその値を読ませる必要があるか?」を基準に、不要であれば encrypted と httponly: true を積極的に使ってセキュアなアプリケーションを作りましょう!