HTTPヘッダー・インジェクション

こんにちは!作者のActiveTKです。

今回は、HTTPヘッダー・インジェクションについて解説しようと思います!

1. HTTPの仕組み

HTTPのレスポンスは、以下のような形式です。

HTTP/{バージョン} {ステータスコード} {ステータス}\n\r
{ヘッダー1}: {内容}\n\r
{ヘッダー2}: {内容}\n\r
..(省略)
{ヘッダーX}: {内容}\n\r
\n\r
{本文}

最初の行に状況が書かれ、その次の行からヘッダーが書かれます。

そして、最初の「\n\r\n\r」からを本文として認識されます。

2. HTTPヘッダー・インジェクション

PHPでは、header関数を使う事でヘッダーを追加する事が出来ます。

例えば、

<?php
header("Location: https://blog.activetk.jp/");

と実行すれば、ヘッダー内にLocationという項目が追加され、

その効果でhttps://blog.activetk.jp/にリダイレクトされます。

ついでに、header関数は行の最後の「\n\r」を自動で付けてくれます。

HTTPの仕様で、\n\rがあると別のヘッダーとして認識されます。

なので、

<?php
header("Test1: hogehoge\n\rTest2: hogehoge");

と実行すれば、「Test1」と「Test2」の両方のヘッダーが追加されます。

では、$_GET[“href”]で指定されたサイトへリダイレクトするサービスを作ったとします。

すると、

<?php
if (isset($_GET["href"]))
{
  header("Location: " . $_GET["href"]);
  exit("redirecting..");
}
else exit("Error.");

というコードになります。

ユーザーが「?href=http://example.com/」と入力したとすると、

HTTP/1.1 301 Moved Permanently\n\r
Connection: Close\n\r
Content-Length: 13\n\r
Location: http://example.com/\n\r
\n\r
redirecting..

というレスポンスになり、正常にリダイレクトされます。

しかし、ユーザーが「?href=http://example.com/%0AHogehoge: This%20is%20test」と入力したとすると、

HTTP/1.1 301 Moved Permanently\n\r
Connection: Close\n\r
Content-Length: 13\n\r
Location: http://example.com/\n\r

Hogehoge: This is test\n\r
\n\r
redirecting..

というレスポンスが生成されます。

よって、「Hogehoge」というヘッダーを送れてしまいます。

ユーザー由来のデータをheader関数で送信すると、関係の無い別のヘッダーまで追加出来てしまうという事です。

しかし、ヘッダーを追加するだけではCookie情報を抜き取ったりする事は出来ません。

悪意のあるページに飛ばす事は可能ですが。

問題はここからです。

ユーザーが「?href=http://example.com/%0A%0A<script>alert(“document.cookie”);</script>」と入力したとすると、

HTTP/1.1 301 Moved Permanently\n\r
Connection: Close\n\r
Content-Length: 13\n\r
Location: http://example.com/\n\r
\n\r

<script>alert("document.cookie");</script>
redirecting..

というレスポンスが生成されます。

この例ではリダイレクトサービスなので、JavaScriptが実行されずにリダイレクトされる可能性が高いですが、このようにJavaScriptを埋め込めてしまいます。

JavaScriptを使用して、「document.cookie」などの情報を送信されてしまうと、セッションハイジャックなどをされてしまう可能性があります。

3. 対策

・ヘッダー内の改行コードを削除する

<?php
if (isset($_GET["href"]))
{
  $url = str_replace("\n\r","", $_GET["href"]);
  header("Location: " . $url);
  exit("redirecting..");
}
else exit("Error.");

・ホワイトリスト方式 (※コードは省略)

・サーバーで、「Content-Length」を指定するように設定する

・nonceを指定して、不正なJavaScriptを実行させない

<?php
$nonce = substr(str_shuffle('1234567890abcdefghijklmnopqrstuvwxyz'), 0, 30);
header("Content-Security-Policy: script-src 'nonce-{$nonce}' 'self' 'strict-dynamic' 'unsafe-inline' 'unsafe-eval'");
?>
<script>alert("ここは実行されない!");</script>
<script nonce="<?=$nonce?>">alert("ここは実行される!");</script>

・Cookieに「HTTP Only」属性をつける

<?php
setcookie("Cookie名", "中身", time()+60*60*24*365, "/", "{サイトのドメイン}", {SSL対応の場合はtrue}, true);

・ユーザーの入力値に頼らない

<?php
if (isset($_GET["href"]))
{
  $url = "";
  if ($_GET["href"] == "type1") $url = "test1.example.com";

  else if ($_GET["href"] == "type2") $url = "test2.example.com";
  else $url = "www.example.com";
  header("Location: http://" . $url);
  exit("redirecting..");
}
else exit("Error.");

最後まで目を通して頂き、誠にありがとうございました。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です