$30 off During Our Annual Pro Sale. View Details »

rack-attack gemによるリクエスト制限の失敗と学び

rack-attack gemによるリクエスト制限の失敗と学び

北陸Ruby会議01にて発表
https://regional.rubykaigi.org/hokuriku01/

Avatar for なっちゃん

なっちゃん

December 15, 2025
Tweet

More Decks by なっちゃん

Other Decks in Programming

Transcript

  1. 今日扱う「リクエスト制限」とは • 対象 ◦ Rails / Rackミドルウェアでやる範囲 ◦ APIの安定運用を目的としたリクエスト制限 ▪

    ユーザーに正しくAPIを利用してもらうための制限 • 対象外 ◦ インフラレイヤのリクエスト制限 ◦ DoS攻撃を防ぐためのセキュリティ対策 4
  2. rack-attack • Rackでリクエスト制限をする定番gem • 柔軟なリクエスト制限を実現できる ◦ throttle (レート制限) も柔軟に定義できる ◦

    怪しい挙動のときだけBANする ◦ 制限に引っかかったときの情報をレスポンスヘッ ダーで返せる 8
  3. 10 class Rack::Attack class ::Rack::Attack::Request # Authorization ヘッダーからトークン文字列を取得 def token_string

    get_header("HTTP_AUTHORIZATION") end end throttle("token", limit: 5000, period: 1.hour) do |req| req.token_string end throttle("subdomain", limit: 50000, period: 1.minute) do |req| req.host end end SmartHRで動いているコードを発表用に加工しています config/initializers/rack_attack.rb
  4. 13 self.throttled_responder = ->(request) { throttle_data = request.env["rack.attack.throttle_data"] if throttle_data.key?("token")

    counts = throttle_data["token"] reset = counts[:period] - (Time.now.to_i % counts[:period]) remaining = (counts[:limit] - counts[:count]).clamp(0..) header["X-Rate-Limit-Limit"] = counts[:limit].to_s header["X-Rate-Limit-Reset"] = reset.to_s header["X-Rate-Limit-Remaining"] = remaining.to_s end if throttle_data.key?("subdomain") # 略 end [429, header, ["Throttled\n"]] } 残り回数などを計算 headerに詰める 制限を超過したらthrottled_responderが 実行されるので、headerに情報を詰める
  5. 17 throttle("token", limit: 5000, period: 1.hour) do |req| req.token_string end

    throttle("token_second", limit: 10, period: 1.second) do |req| req.token_string end throttle("subdomain", limit: 50000, period: 1.minute) do |req| req.host end 追加 throttleの定義を追加
  6. 18 self.throttled_responder = ->(request) { throttle_data = request.env["rack.attack.throttle_data"] if throttle_data.key?("token")

    # 略 end if throttle_data.key?("token_second") # 略 end if throttle_data.key?("subdomain") # 略 end [429, header, ["Throttled\n"]] } 追加 headerに情報を詰める