Rackのバージョンアップに伴い一部のリクエストが拒否されるようになった話
こんにちは、株式会社TRUSTDOCKでバックエンドエンジニアをやっている渡瀬です。
Railsで稼働しているシステムにおいて、Rackのバージョンアップに伴い、4MBを超える一部のリクエストが拒否される事象が発生しました。
本記事ではその内容についてご紹介いたします。
概要
- Rackのバージョンアップ後、4MBを超える
application/x-www-form-urlencodedのPOSTリクエストを送信するとエラーで拒否されるようになった - エラーレスポンスが返されるものの、Railsアプリケーション層(Controllerなど)まで到達していなかった
事象の経緯
RailsシステムへのAPIリクエストでエラーが発生していることが判明し、調査を開始しました。
確認したところ、Railsのアプリケーションログには異常が記録されておらず、前段のNginxのログにはエラーが出力されていました。そのため、当初はNginxの設定等により拒否されているものと考えていました。
しかし、Nginxの設定に思い当たる節がなく、ログを詳細に分析すると、以下の傾向に気づきました。
- データサイズが4MBを超える一部のリクエストでエラーが発生していた
- エラーとなったリクエストでは
Content-Typeがapplication/x-www-form-urlencodedとなっていた -
Content-Typeがapplication/jsonの場合は、サイズが大きくてもエラーが発生していなかった
問題の原因
調査の結果、Rackのバージョンアップ(2.2.14 以降)により、セキュリティ上の理由から application/x-www-form-urlencoded 形式のPOSTリクエストに対してデフォルトのサイズ制限が導入されたことが原因であると判明しました。
以前のバージョン(2.2.13まで)では処理可能だった4MBを超えるリクエストが、バージョンアップ後はRack層でエラーとして処理されるようになっていました。
何が変わったのか
クエリ文字列や application/x-www-form-urlencoded ボディの解析において、「バイトサイズ」および「パラメータ件数」に制限が設けられました。
- バイトサイズの上限:4MB
- パラメータ数の上限:4,096個
なぜこの変更が入ったのか?
従来のRackでは、クエリ文字列やリクエストパラメータを実質的に無制限で解析していたため、大量のパラメータを送信されるとCPUやメモリを過剰に消費し、DoS攻撃につながる恐れがありました。
今回の変更は、これらのリソース消費に上限を設けることで、リスクを軽減するためのセキュリティ対策です。
なぜ JSON ではエラーにならないのか?
この制限は、Rackが application/x-www-form-urlencoded や multipart/form-data を解析して params ハッシュを構築する際の処理に適用されます。
一方、application/json のリクエストボディは、Rackではパースされず、通常Railsなどのフレームワーク側でパースされるため、今回のバージョンアップによる制限の影響を受けなかったと考えられます。
なぜアプリケーションログが出力されなかったのか
今回の事象で調査が難航した要因は、アプリケーション層でログが出力されなかったことです。
リクエストの処理フローは概ね以下のようになっています。
クライアント
↓
Webサーバー(Nginx)
↓
アプリケーションサーバー(Puma, Unicorn など)
↓
Rack Middleware(★ここでリクエスト解析時にサイズ制限チェック)
↓
Rails Router
↓
Controller
↓
Application Code(通常はこの層でアプリケーションログが出力される)
RackミドルウェアはRailsアプリケーションの前段で動作します。リクエストの解析とサイズ制限チェックがRack層で行われるため、制限を超えたリクエストはRailsのアプリケーションコード(Controller等)に到達する前に例外が発生し、拒否されます。
その結果、Railsのロガーまで到達せず、アプリケーションログにエラーが記録されませんでした。
対応と学び
その後、 Content-Type を application/x-www-form-urlencoded から application/json へと変更することで4MBを超えるリクエストでのエラーは解消しました。
実は調査の初期段階で、エラーが発生するようになった時期にRackを含む複数のライブラリがアップデートされていることに気づきました。
しかし、アプリケーションログにエラーが出力されていなかったため、リクエストがRailsまで到達していないと安易に考えてしまいました。
今後は、アプリケーションログに出ていない場合でも、Webサーバーとアプリケーションの間にあるミドルウェア層で弾かれている可能性を考慮に入れたいと思います。
また、Content-Type 以外においても想定と異なるリクエストが来ていないかを継続的に監視し、システムの安定運用に努めていきたいと考えています。
最後に
Railsアプリケーションを運用していると、アプリケーションコードだけでなく、Rackなどのミドルウェア層での仕様変更や問題に遭遇することがあります。
特にセキュリティアップデートに伴うデフォルト設定の変更は影響範囲が見えづらいため、リリースノートの確認や適切な監視が重要です。
この記事が同じような問題に遭遇した方の助けになれば幸いです。
最後までお読みいただきありがとうございました!
Discussion