セッションハイジャックを防ぐ7つの方法

会社のブログに寄稿させていただきました。
PHPのセッション情報をデータベース(MySQL)に保存するぜ!

こちらの記事ではセッション情報をDBサーバーに保存させて、ロードバランサーで接続するサーバーが切り替わってもセッションが維持される方法について記載しましたが、セッションハイジャックについては言及しておりません。ということで今回はセッションハイジャックを防ぐ方法について紹介します。

セッションハイジャックとはなにか

そもそもセッションハイジャックとはなにか。cookieでセッション管理を行うPHPの場合はcookieにセッションIDを保存します。このセッションIDを他のユーザーのものに書き換えてなりすましてしまうのがセッションハイジャックです。cookie情報なんてのはブラウザで簡単に書き換えられてしまいますからね。

cookie

セッションハイジャックにおける対策としては大まかに分けて2つの対策、まずセッションIDを見抜かれにくい様にする方法、そしてセッションIDを見ぬかれた場合でもアプリケーションにログインさせないようにする方法を行う必要があります。

1. セッションIDを簡単に予測出来るものにしない

まずセッションIDは簡単に予測されるものであってはなりません。php5.5.1以降でsession_set_save_handlerを使ってcreate_sidコールバックで独自のセッションIDが割り振れる様になりました。もしこのcreate_sidコールバックを使用している場合は注意が必要です。数字や連番等の簡単なものをそのままIDにしていると、適当に書き換えただけで他のユーザーのセッションIDになってしまう確率が高くなります。

cookie01

総当り攻撃を食らったらひとたまりもないですね。乱数・アクセス日時・IPアドレス等を使ってハッシュ化する等、簡単に予測が出来ないものにする必要があります。

2. session.use_trans_sidを1にしない

php.iniの設定です。PHPのセッションはcookieで管理しているとかいいながらですが、コレをやるとURLでセッション管理を行うようになってしまうのです。

まあ、デフォルト0なんであえて1にしているパターンもほとんど無いかと思いますが、こちらを設定しているとURLにセッションIDが付加されるようになってしまいます。URLに付加されると。。。

session02

見ただけでセッションIDがバレちゃいますね。また、ユーザーが知らずにURLをコピペで公開した場合に、それを使った他のユーザーがそのユーザーになりすましてしまうっていうことになってしまいます。

URLにセッションIDを付加しないようにするにはsession.use_trans_sidを0にしておきましょう。また、session.use_cookies、session.use_only_cookiesも同様に設定しておきましょう。

; URLにセッションIDを付加しない(デフォルト:0)。
session.use_trans_sid = 0

; セッションIDの保存にcookieを使用する(デフォルト:1)。
session.use_cookies = 1

; セッションIDの保存にcookieのみを使用することを指定(デフォルト:1)。
session.use_only_cookies = 1

デフォルトのまま変更は必要ないですね。

3. session.nameを変更する

session.nameの値がcookieのキーとして保存されますので、デフォルトの「PHPSESSID」のままだとセッションIDのcookieだとすぐに特定されてしまいます。

この値を変更しておくことでセッションIDのcookieが特定されにくくなります。

session.name = abcdefg

session03

4. session.cookie_secureを1にする

session.cookie_secure = 1

この設定を行うだけでHTTPS環境でのcookieにsecure属性が付くので、HTTPS通信時にのみcookieが送信されるようになります。これによりパケット盗聴によるセッションIDの特定を防ぐことが出来ます。

HTTPS環境で動作しているサービスでは必ず設定しておきたいところですが、HTTP環境も混在する場合は注意が必要です。session.cookie_secureを1にした場合のHTTP環境でのcookieはアクセスする度に値が書き換えられてしまうのでセッションIDも書き換えられてしまい、セッションを継続することができなくなってしまうのです。

5. session.use_strict_modeを1にする

session.use_strict_mode = 1

session.use_strict_modeを1(有効)にすることで、PHPが初期化したセッションID以外のセッションIDは受け付けなくなります。逆に言うと0(無効)の場合はユーザーが設定したセッションIDでセッション情報が初期化されてしまうということです。

session04

ブラウザで書き換えたセッションIDでアクセスすると、

session05

サーバー上で書き換えられたセッションIDでセッション情報が作成されてしまいました。

これを利用した攻撃がセッションアダプションです。セッションアダプション攻撃は特定のセッションIDを発行するスクリプトをユーザーに実行させ、そのユーザーがログインした後にそのセッションIDを使ってなりすます攻撃です。

これを回避するためにsession.use_strict_modeを1(有効)にし、PHPが発行したセッションID以外は無効にすることが必要です。

6. セッションIDをワンタイム化する

セッションIDをアクセス毎に変更することで更にセッションIDの特定を困難にします。セッションIDを変更するにはsession_regenerate_id関数で簡単に再発行できます。

<?php
session_regenerate_id(true);

引数にtrueを指定しないと、以前のセッションデータがサーバー上に残ったままになってしまいますので注意しましょう。

また、セッションIDをワンタイム化した際の注意点として、連続アクセス(リンクを連打したり)した場合にほぼ必ずセッションが切れてしまいます。これはサーバー上でセッションIDを切り替えるスクリプトが実行された後に古いセッションIDが送信されて来るので、セッション情報が見つからず新しいセッション情報が発行されてしまうためです。

これを防ぐためにはJavascriptで連打されないようなものを実装するか、サーバー側でのセッションIDの変更のタイミングを一定の確率で発生させるようにする等の対応が必要です。

7. セッションにユーザー固有の情報を保存し検証する

それでもセッションIDが見抜かれてしまった場合の対策です。

セッション情報に$_SERVER[‘REMOTE_ADDR’]等の情報を保存するようにしてログインチェックの検証を行うようにすれば、セッションIDを見抜かれてしまっても不正な操作を防ぐ事が可能です。

<?php
session_start();
// ログイン処理後
$_SESSION['ipaddress'] = $_SERVER['REMOTE_ADDR'];
<?php
session_start();
// ログイン検証後
if($_SESSION['ipaddress'] !== $_SERVER['REMOTE_ADDR'])
{
    exit('ログイン検証に失敗しました!');
}

まとめ

・セッションIDは簡単に見抜かれないようにする工夫が必要。

・それでも見抜かれた場合はアプリケーション側でもなんとか出来る仕組みが必要。

; URLにセッションIDを付加しない(デフォルト:0)。
session.use_trans_sid = 0

; セッションIDの保存にcookieを使用する(デフォルト:1)。
session.use_cookies = 1

; セッションIDの保存にcookieのみを使用することを指定(デフォルト:1)。
session.use_only_cookies = 1

; セッションIDの名前変更(デフォルト:PHPSESSID)
session.name = abcdefg

; HTTPS以外でのcookieを送信しない(デフォルト:0)
; 但しHTTPSのみの環境に限る
session.cookie_secure = 1

; セッションアダプション対策(デフォルト:0)
session.use_strict_mode = 1

コメントを残す