【Rails】APIモードでHttpOnlyなCookieを削除する
実現したいこと
- サーバーサイド(Rails)側でCookieを削除できるようにしたい
 
背景
- これまではフロントエンド側でCookieの値の操作を行っていた
 - 脆弱性対策でCookieにHttpOnly属性を設定したことによって、フロントエンド側(Nuxt3)でCookieを参照することが出来なくなった
 - 認証情報の受け渡し方法としてAuthorizationヘッダーの利用も取りやめた
 - 従って、HttpOnly属性付きのCookieはJavaScript(フロントエンド側)からの操作ができないため、必ずAPI経由でサーバーサイド(Rails)から削除する必要がある
 
結論
Frontend
APIリクエスト送信時に、withCredentials: trueオプションを追加し、APIリクエストにCookie情報を含めて送信できるようにする。これにより、クロスオリジン間でもCookieが正しく送受信される。
await this.$api.v1.deleteCookie({ withCredentials: true }); // ⭐️API呼び出し時にCookie情報を有効化
Backend
RailsのAPIモードでは、デフォルトでCookieの書き込みができない。
そのため、ActionDispatch::Cookiesミドルウェアを有効化し、APIリクエストでもCookieを操作(削除含む)できるようにする。
config.middleware.use ActionDispatch::Cookies # ⭐️この行を追加し、Cookieの書き込みを有効化
# frozen_string_literal: true
class V1::XXXController < V1::ApplicationController
  before_action :require_login, only: :destroy
  def destroy
    # ⭐️Cookieを削除
    cookies.delete(Settings.token,
                   domain: ".sample.com",
                   path: "/",
                   secure: true,
                   httponly: true,
                   same_site: :lax,
                   expires: Time.zone.at(0))
    render_success
  end
  private
  def require_login
    render_redirection(Api::V1::Response::Code::REQUIRE_LOGIN) unless logged_in?
  end
end
過程 (解決までの道のり)
cookies.deleteしてもブラウザ上のCookieが消えない...!
- フロントエンド側でAPI呼び出し時に
withCredentials: trueも渡している- APIのレスポンスも問題なし(
status: 200がちゃんと返ってきている) 
 - APIのレスポンスも問題なし(
 - 
ActionController::Cookiesはincludeしていたので、読み取り自体はできる 
class V1::ApplicationController < ActionController::API
  include ActionController::Cookies
- 
cookies[Settings.token]を参照した時に値は消えている 
# frozen_string_literal: true
class V1::XXXController < V1::ApplicationController
  before_action :require_login, only: :destroy
  def destroy
    Rails.logger.info("before: #{cookies[Settings.token]}") # before: DcxeHX9kvuyEK3DcHgCeQmFpFkm
    cookies.delete(Settings.token,
                   domain: ".sample.com",
                   path: "/",
                   secure: true,
                   httponly: true,
                   same_site: :lax,
                   expires: Time.zone.at(0))
    Rails.logger.info("after: #{cookies[Settings.token]}") # after: 
ブラウザ上(開発者ツール)で確認した時に該当のCookieが消えないのは何故...?
暫定
- 
response.headersが持つSet-Cookieを明示的に削除 
# frozen_string_literal: true
class V1::XXXController < V1::ApplicationController
  before_action :require_login, only: :destroy
  def destroy
    response.headers["Set-Cookie"] = "#{Settings.token}=; domain=.sample.com; path=/; Max-Age=0; Secure; HttpOnly;"
一応これでいける
・・いやいやcookies.deleteで消す方法絶対あるやろ!(なぜ関西弁?)
シランケド。🏇
解決
RailsのAPIモードだと、ミドルウェアを有効にしないとCookieの書き込みができないということが判明。これは、APIモードがデフォルトでセッションやCookieの処理を行わない設計になっているため。
そこで、config/application.rbに以下の行を追加
config.middleware.use ActionDispatch::Cookies
「これで削除されるはず!」と期待したが、残念ながらまだCookieは消えず・・
なんでやねん!(関西弁◎)
・・あ、設定変えてるからコンテナの再起動が必要かもしれない💡
(settings.ymlの内容等を変更した時は普段Sidekiqを再起動している)
早速、Dockerコンテナを再起動してみる
docker compose restart
コンテナ再起動後、無事にブラウザ上のCookieが削除されていることを確認🎉🎉🎉
参考
株式会社ウェイブのエンジニアによるテックブログです。 弊社では、電子コミック、アニメ配信などのエンタメコンテンツを自社開発で運営しております! wwwave.jp/service/
Discussion