PHPのセッションアダプションによって任意のセッションIDが受入れられる | A Day In The Boy's Life

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

昨年末にPHPのセッションアダプションの話題が出ていて、そういった記事を読んでいるうちに自分の環境を見直してみると全然理解していない部分があったんだなということで、その辺をまとめてみたいと思います。


PHPのセッションアダプション脆弱性は修正して当然の脆弱性 @ yohgaki's blog



PHPの未初期化のセッションIDは何でも受け入れられる


セッションアダプションの問題の根幹はこの部分にあって、セッションIDはプログラム側で作られるものだと思い込んでたものが、Cookieを通して任意のセッションIDを送り込まれてそのまま利用されてしまうという問題です。

確認(攻撃)方法はいたって簡単で、PHPのセッション管理をCookieベースで行っている場合に、そのCookie内のセッション変数(php.iniのsession.name)に任意の文字列を入れて送り込めば、そのセッションIDで利用が開始されてしまいます。


例えば、よくあるログイン管理のプログラムで、


<?php

if (checkLogin($_POST['id'], $_POST['password'])) {
    session_start();
    $sess_id = session_id();
}


というような処理をしていた場合、通常$sess_idにはsession_id() が作り出す26桁の英数字の文字列が返されることを期待していますが、下記のようにCookieのセッション用のパラメータに予め任意の文字を指定してリクエストを送信するとそのセッションID(mySessionId)がsession_idにより返されてしまいます。


A Day In The Boy&#39;s Life-任意のセッションIDをCookieに埋め込む


もちろん、Cookieのセッション変数名がなんであるかわかっていないといけませんし、ログインは成功しないといけないわけですけど、よく利用しているサイトであればそれは簡単にわかるわけなので、任意のセッションIDを流し込んで最悪セッションハイジャックができたりしてしまいます。

ログインが成功して発行したセッションIDは絶対だから安心なんて思っていると簡単に書き換えが可能なわけです。



セッションアダプションを無効化する


このセッションIDを固定化する攻撃の問題点は、先のエントリと同じ大垣さんが書かれた下記の記事がわかりやすいです。


第25回 PHPのアキレス腱 ── セッション管理 @ gihyo.jp


簡単に言ってしまえば、PHPのセッションで使うためのものと同名のCookieが複数発行されてしまえば、任意のセッションIDで利用されてしまう可能性があるというものです。


A Day In The Boy&#39;s Life-任意のセッションIDを送り込んだ場合


上記の場合、「/」で発行されたのが本来のセッションIDで、それを「/2013」のパスで発行したCookieにより上書きしています。

利用されるセッションIDは、出力結果のように任意で送り込んだセッションIDの方です。


この対処として、1つは発行されたCookieを事前に削除するようにしておくこと、もう1つはsession_regenerate_id() を利用して、セッションIDを毎回再発行することです。


<?php

if (checkLogin($_POST['id'], $_POST['password'])) {
    session_start();
    setcookie("PHPSESSID", "", time() -3600, "/2013", "www.example.com");
    setcookie("PHPSESSID", "", time() -3600, "/", "www.example.com");
    session_regenerate_id(TRUE);
    $sess_id = session_id();
}


ただし、Cookieは発行している全てのCookieを削除しないと意味がありませんが(しかし、それはアプリの構成によっては非常に手間がかかるものですので、発行するCookieの箇所を一元化しておくことと、ここのアプリの中でXSSなどのセキュリティホールを生まないようにしておく必要もあったりするわけですが)。


session_regenerate_id()を利用することで、毎回異なるセッションIDが発行されます。

たとえ、前回のセッションできちんとログアウトができていなかったとしても、セッションIDは新しいものに置き換わります(引数にTRUEを指定することで、過去のセッションは削除されます)。

サイト内で多重ログインするような仕様があったらその時点でセッションIDが変わってしまうので、アプリ内部での挙動も気をつける必要が出てくるかもしれません。


セッションは、ログインを管理する重要な要素なのでかなり重要なのですが、そのセッションの管理って結構PHPにまかせっきりに考えていたりするのに、PHP側であまりうまいことやってくれていないので、このようなアプリ側での配慮も必要になってきています。