意図せぬレスポンスボディを含むリダイレクト応答
2009年8月7日(金曜日)
意図せぬレスポンスボディを含むリダイレクト応答
更新: 2025年1月7日21時6分頃
「ダチョウ式リダイレクトと名付けてみる修行 (d.hatena.ne.jp)」。
ここで言っている「ダチョウ式リダイレクト」とは、リダイレクト応答のレスポンスボディに意図せぬものが出力されている状態を指します。「頭隠して尻隠さず」という言葉がありますが、英語では "To bury one's head ostrich-like in the sand." と言うそうで、ostrich はダチョウですね。
ステータスコードが 301 や 302 などになっていて、Location: フィールドが出力されていれば、レスポンスボディが何であれリダイレクトは行われます。この場合、多くのブラウザではレスポンスボディは見えませんが、パケットを見ればレスポンスボディは丸見えという状態です。普通にブラウザで見ていても分からないので、テストでも発見しにくい厄介な不具合になります。
特に注意したいのは、ログインが必要なページで、未ログイン時にログインフォームへリダイレクトしているようなケース。このようなケースで「ダチョウ式」が発生すると、未ログイン時にはアクセスを拒否してリダイレクトしている……と思わせつつ、実はレスポンスボディにはログイン後画面の内容が出力されてしまっている、などという事が発生し得ます。ログインしていないのにログイン後画面の内容が取得できてしまうので、大変まずいことになります。
ところで、Perlのフレームワークとして有名なものにCatalystというものがあります。Catalystで認証機能を実装したいなぁ、などと考えて「Catalyst 認証」で検索すると、「Catalyst::Manual::Cookbook - Catalystクックブック (www.tcool.org)」がヒットします。これは、少し古い Catalyst-5.62 の Catalyst::Manual::Cookbook の和訳です。
このドキュメントには「ユーザにかならずログインしてもらう」という項目があって、以下のようなサンプルコードが掲げられています。
sub auto : Private { my ($self, $c) = @_; my $login_path = 'user/login'; # 実際にログインページにたどり着けるようにします! if ($c->req->path eq $login_path) { return 1; } # ユーザが登録されていればOKです if ( $c->req->user ) { $c->session->{'authed_user'} = MyApp::M::MyDB::Customer->retrieve( 'username' => $c->req->user ); } # そうでなければログインしていないということ else { # force the login screen to be shown $c->res->redirect($c->req->base . $login_path); } # 処理を続行します return 1; }
このコードには大いなる罠が潜んでいるわけです。ログインしていないとき、$c->res->redirect($c->req->base . $login_path); でリダイレクトしていますが、その後に return 0; していないので、autoの前処理が終わった後に普通の処理が実行されます。つまり、リダイレクト応答しつつも、ログインしているときと同様のレスポンスボディが出力されてしまう可能性があります。
※もっとも、ログインしていることを前提とした処理になっている場合、ログインセッションがないために例外になってセーフ、ということもあります。その辺りは運です。:-)
単に return 0; が抜けただけのケアレスミスだと思うのですが、そのまま使うと大変です。ちなみに、最新のCatalyst::Manual::Cookbook (search.cpan.org)では、このコードはなくなっているようです。
※そういえばhatomaru.dllもリダイレクト応答はだいぶ手抜きですね。まあ、リダイレクト以外も全体的に手抜きですが。
※追記: CWE では "Redirect Without Exit" と呼ばれている模様: CWE-698: Redirect Without Exit (cwe.mitre.org)
- 「意図せぬレスポンスボディを含むリダイレクト応答」にコメントを書く
- 前(古い): 新たに話題の地図を入手……してた
- 次(新しい): ムダヅモ無き改革2