初心者データサイエンティストの備忘録

調べたことは全部ここに書いて自分の辞書を作る

認証・認可の不備とHTTPヘッダ・インジェクション

認証・認可の不備とHTTPヘッダ・インジェクションについて勉強しました。本記事はこれらの攻撃についてまとめています。なお、私はセキュリティに関してはずぶの素人で本記事にも誤りが含まれているかもしれません。その際はコメントでご指摘いただけると幸いです。

認証・認可の不備

認証と認可の不備について説明するために、まずは認証と認可の違いについて述べます。

認証と認可の違い

認証はユーザを識別する仕組みです。例えば、ログイン時にIDとパスワードを要求し、これらを正しく入力できた人にログインを許可するというのが認証です。

一方で、認可とは権限を持っている人にしか情報を表示しないことを指します。例えば、AさんにはAさんの個人情報しか見ることができないようにするのが認可です。 認証と認可の違いは図1で説明します。

図1:認証と認可の違い

どんな攻撃

認証の不備があるとログインされやすくなります。例えば、ログイン時にメールアドレスなどの第三者が知りうる情報のみを利用する場合は、正規のユーザでなくとも容易にログインできてしまいます。 また、認可の不備があると直接URLを入力するなどして、本来表示できないはずのページが表示されるなどの問題が発生してしまいます。

どんな影響があるの?

AppGoatによれば、下記の影響があるようです。

  • ユーザの非公開情報を閲覧される
  • 権限のあるユーザのみが編集可能な情報を改ざん、消去される

対策

認証・認可の不備の対策は、(当たり前ですが)必要な認証・認可の制御機構をWEBサーバ上に設けることです。

認証機構を持たせる例

認証機構は、以下の2つの処理を組み合わせることで実現されます。

1つ目は、ログイン時にWEBサーバ上でセッション情報を発行する処理です。 2つ目は、閲覧にログインが必要なページをユーザが開くときに、WEBサーバがセッション情報を確認するような処理です。

ログイン時にセッション情報を発行し、ログインが必要なページを開くときはそのセッション情報を確認して閲覧の許可・不許可を出すというイメージです(図2)。

図2:2つの処理のイメージ

この2つの処理を実現するコードは下記です。

// ログイン処理
session_regenerate_id(true);
$_SESSION["id"]=$_POST["id];
//閲覧にログインが必要なページを開くとき
session_start();
if(isset($_SESSION["id"])){ //セッション情報の確認
    print("ようこそ" . $_SESSION["id"] . "さん")
}

認可機構を持たせる例

認可機構については、ページを表示する際に権限チェックを行うことで実現します。 例えば、adminユーザ以外にはページを表示したくないときは下記のようなコードを実装します。

} elseif ($session[$this->get_login()]["auth"] !== "admin") {
            $this->set_session($this->get_login(), false);
            $this->set_page(parent::PAGE_LOGIN);
            $this->set_content(parent::WARNING12, true);
            return $this;

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

どんな攻撃

HTTPリクエストにHTTPヘッダを書き換えるような情報を含ませることで、HTTPヘッダを書き換える攻撃です。

HTTPリクエストを送るURLのパラメータの一部を用いて、HTTPヘッダを構成するWEBサーバがあるとします。 このとき、

https//hogehoge?url=test.html%0D%0ASet-Cookie:PHPSESSID=123ABC

などのURLを送ると改行コード%0D%0Aより後ろの部分がHTTPヘッダの一部として出力されてしまいます。 上の例だと

Set-Cookie:PHPSESSID=123ABC

がHTTPヘッダに含まれることで、Cookieの値が悪意ある者によって勝手に123ABCにされてしまいます。

どんな影響があるの?

AppGoatによれば、次の影響があるそうです。

  • 任意のCookie発行
  • 表示内容の改ざん
  • キャッシュサーバのキャッシュ汚染

対策

AppGoatによれば次の対策があるようです。

  • HTTPレスポンスヘッダには外部からのパラメータを出力しない
  • 専用APIを用いてリダイレクトやCookie出力する
  • ヘッダを生成する外部からのパラメータの改行文字をチェックする

2番目について少し補足します。
2番目を理解するには、HTTPヘッダにはリダイレクトやCookieなどの情報が含まれているということを知っておく必要があります。 HTTPヘッダにリダイレクトやCookieの情報が書かれているため、HTTPヘッダ・インジェクションを使って偽のサイトへリダイレクトさせたり、先に述べたようにCookieを書き換えることができます。 それを防ぐため、リダイレクトやCookieに関する情報は外部から書き換えられないようにするというのが2番目の対策です。PHP5.5以降はheader()関数によってそれを実現することができます。

ここでは、上で述べた対策のうち、3番目の「ヘッダを生成する外部からのパラメータの改行文字をチェックする」についてコードを用いて説明します。

まず、脆弱性のあるコードです。

// 脆弱性のあるコード
header('Location:' . $url)

このコードは$urlを直接参照しており、URLに含まれるパラメータの改行文字等については何もチェックをしていません。したがって、

https//hogehoge?url=test.html%0D%0ASet-Cookie:PHPSESSID=123ABC

などのHTTPリクエストによってHTTPヘッダ・インジェクションをされてしまう可能性があります。 そこで、上記のコードを下記のように書き換えます。

// 修正したコード
if(preg_match('/\r|/\n', $url)){
    die('Bad URL');
}

これにより改行コードが含まれるURLが入力されたときに処理を停止することができます。 なお、PHP5.5以降ではheader()が自動的にこの処理をしてくれるようになっています。なので、PHP5.4より前のバージョンを使うときは上記のような処理が必要となります。

まとめ

認証・認可の不備とHTTPヘッダ・インジェクションについてまとめました。
認証・認可の不備はユーザを判断する認証と、権限がある人にのみページ閲覧を許可する認可の仕組みが抜けていることによる脆弱性でした。
また、HTTPヘッダ・インジェクションはHTTPヘッダを書き換えるようなHTTPリクエストを送ることで、Cookieを固定化するなどの攻撃でした。