Ramazeさんで"HTTP Cache Poisoning via Host Header Injection"をやってみました

15KB Of Fame: HTTP Cache Poisoning via Host Header Injection という記事を読みました。これは、HTTP GET を発行する時に HOST を好きに指定することで、サーバ側のレンダリング結果のキャッシュを汚染しちゃって色々できちゃわない?というものです。HTTP_HOST に変なのが入ってるとはあんまり考えないだろうという油断を突いたもの、ということでしょうか。

元記事のヤツだと具体的にどういう事か分かりづらいかと思いますので、これをRamazeさんを使って再現してみることにします。

require "rubygems"
require "ramaze"

class MyController < Ramaze::Controller
  map :/
  engine :None
  helper :cache
  # 次の指定により http://localhost:7000/ のレンダリング結果は
  # キャッシュされ再利用されます 
  cache :index

  def index
    <<-__HTML__
<html><body>
HOST: #{request.env["HTTP_HOST"]}
</body></html>
    __HTML__
  end
end

Ramaze.start

以上のような内容の Ramaze アプリ(test.rb)を起動します。

% ruby test.rb
[2008-06-11 16:55:07] INFO   Starting up Ramaze (Version 0.3.9.1)
[2008-06-11 16:55:07] WARN   Public root: 'public' doesn't exist
[2008-06-11 16:55:07] WARN   Template root: 'view' doesn't exist
[2008-06-11 16:55:07] DEBUG  mapped Controllers: {"/"=>MyController}
[2008-06-11 16:55:07] INFO   Ramaze is ready to run on: 0.0.0.0:7000
...

というわけで、サーバは http://localhost:7000/ で待ち構えています。では準備が整いました。

まず悪い人が telnet かなにかを使ってサーバに接続します。

% telnet localhost 7000

ここで次のようなリクエストを発行します。

GET / HTTP/1.1
Host: example.com

するとサーバは request.env["HTTP_HOST"] を鵜呑みにしてレンダリングしますので、次のような内容が返ってきます。

<html><body>
HOST: example.com
</body></html>

さて、このレンダリング結果はサーバ側にキャッシュされています。そこで、良い子の人が普通のブラウザで http://localhost:7000/ にアクセスしますと、キャッシュされたものがそのまま返ってきますので、やはり次のような内容が返ってきてしまいます。

<html><body>
HOST: example.com
</body></html>

すると、わー、localhost:7000 に接続したはずなのにびっくり!という事になってしまうわけです。

ということなのですが、これを完成させるためには、悪い人は非常にタイミング良くリクエストを投げてやる必要があると思います。正直結構難しいのではないでしょうか。あと、request.env["HTTP_HOST"] を直接レンダリングする場合って少ないと思うのですが、そうでもないのでしょうか。

脆弱性って言う程でもない気がするのですが、気をつけておいた方が無難だろう、とは思いました。