PHP のセッションを使ったログイン認証はなぜ安全なのか?

PHP
スポンサーリンク

PHP のセッションを使ったログイン認証後の処理は、上のように簡単なコードで実装できてしまいます。利用者の大切な情報を守るための、とても重要な処理なのですが、こんな簡単なコードで大丈夫なのかと不安になるのは私だけでしょうか(^^;) そこで今回は、PHP のセッションを使ったログイン認証がなぜ安全なのかを、PHP のセッションの仕組みをふまえて検証してみました。

PHP のセッションの仕組み

まずはじめに、簡単な例で PHP のセッションの仕組みを確認しておきましょう。

セッションIDのセット

例えば次のような PHP が Webサーバーに設置されていたとします。

<?php
session_start();
$_SESSION['sample'] = 'サンプル';

1行目の「session_start()」でセッションを開始して、2行目でセッション変数 $_SESSION['sample'] に文字列「サンプル」をセットしています。

この PHP にブラウザでアクセスすると、Webサーバーは「sess_<セッションID>」という名前の セッションファイルをサーバー上に作成 します。そして、これと同じセッションIDを、ブラウザのクッキーに「PHPSESSID=<セッションID>」のようにセットします。

セッションファイルを作成し、ブラウザにセッションIDをセットしている図

具体的には、Webサーバーの「/var/lib/php/session/」の下に(場所は環境によって異なります)次のようなセッションファイルが作成されます。(青字の部分がセッションIDです)

● Webサーバーに作成されたセッションファイル

sudo ls -l /var/lib/php/session/
-rw------- 1 apache apache 27  7月  2 03:47 sess_hofrmg3ugisp41v8h6k00n67l2

そしてブラウザには、Webサーバーからのレスポンスヘッダ「Set-Cookie:」で、同じセッションIDがクッキーにセットされます。

● ブラウザにセットされたクッキー

HTTP/1.1 200 OK
Date: Mon, 01 Jul 2019 18:47:46 GMT
Server: Apache
Strict-Transport-Security: max-age=15768000
Set-Cookie: PHPSESSID=hofrmg3ugisp41v8h6k00n67l2; path=/
(略)

セッション変数の保存場所

さて、「$_SESSION['sample'] = 'サンプル';」で、セットしたセッション変数は、どこに保存されているのでしょうか?

セッションIDをクッキーに保存するので、セッション変数もクッキーに保存されているとよく勘違いしてしまうのですが、セッション変数は、サーバー上に作成されたセッションファイルの中に保存されています。そのため利用者が、セッション変数を変更することは絶対にできません。(これに対してクッキーは利用者が自由に変更できます)

sudo cat /var/lib/php/session/sess_hofrmg3ugisp41v8h6k00n67l2
 
sample|s:12:"サンプル"; ←セッション変数

セッション変数の取り出し

保存されているセッション変数は、次のような PHP で取り出すことができます。( session_start() をする以外は、普通の変数と同じように扱えます)

<?php
session_start();
echo $_SESSION['sample'];

上の PHP にブラウザでアクセスすると、ブラウザは先ほどクッキー(Cookie)にセットしたセッションIDを次のようなリクエストで Webサーバーに送信します。

Host: www.apar.jp
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: PHPSESSID=hofrmg3ugisp41v8h6k00n67l2
(略)

セッションIDを受け取った Webサーバーは、それに該当するセッションファイル「sess_<セッションID>」から、セッション変数を取り出してブラウザへ返し、ブラウザにセッション変数が表示されます。

セッション変数を表示している図

以上が PHP セッションの仕組みです。ポイントは次の2点です。

・セッション変数は、セッションIDがセットされたブラウザからのみアクセスができる
・セッション変数は、サーバー上に保存されるため利用者でも変更ができない

PHP のセッションを使ったログイン認証

PHP のセッションを使ったログイン認証の例として、下のようなユーザー情報が保存されているデータベースのテーブルがあるとします。

ユーザー情報のテーブル

(よく説明のためにデータベースにパスワードを保存している例がありますが、実際にパスワードそのものをデータベースに保存してはいけません。一般的には password_hash 関数などでパスワードをハッシュしたものを保存します。)

このユーザー情報テーブルに対する PHP のセッションを使ったログイン認証は、次のようなコードになります。(細かい処理は省略しています)

<?php
$user_id    = filter_input(INPUT_POST, 'user_id');  // 入力されたユーザーID
$password   = filter_input(INPUT_POST, 'password'); // 入力されたパスワード

$hash = '<入力されたユーザーIDをキーにして、データベースから取り出したパスワードハッシュ>';

// パスワードの検証
if ( ! password_verify($password, $hash) ) {
    die('ユーザーIDまたはパスワードが間違っています');
}

// ログイン認証成功の処理
session_start();
session_regenerate_id(true); // セッションIDをふりなおす
$_SESSION['user_id'] = $user_id; // ユーザーIDをセッション変数にセット

echo 'ログインしました!';

実際には認証以外の処理が色々はいりますが、ログイン認証に成功したら、session_regenerate_id 関数でセッションIDをふりなおして(こうすることでセッションIDが盗まれることを防いでいます)セッション変数に、ユーザーを識別できる値(上の例ではユーザーID)をセットするだけでOKです。

安全性の検証

さて、このような PHP のセッションを使ったログイン認証は安全なのでしょうか? PHP セッションの仕組みをおさらいしておきましょう。

・セッション変数は、セッションIDがセットされたブラウザからのみアクセスができる
・セッション変数は、サーバー上に保存されるため利用者でも変更ができない

ログイン認証が必要なページの先頭では、下のようにセッション変数に値がセットされていることをチェックするだけで、アクセスしたユーザーがログイン済みであることを確認し、ログイン認証されていないユーザーをブロックすることができます。

<?php
session_start();
if ( ! $_SESSION['user_id'] ) {
    die('ログインしてください');
}
// 以下ログイン認証が必要なページの内容

実に簡単なコードですが、このセッション変数 $_SESSION['user_id'] には、ログイン認証に成功し、セッションIDがセットされたユーザーのブラウザからのみしかアクセスできないため安全です。(ただし注意点もあります)

また、ユーザー情報の変更ページなどでは、セッション変数に保存されたユーザーIDをキーにして、下のようなSQLで名前や住所などをデーターベースから取り出して表示します。

SELECT 名前, 住所 FROM ユーザーテーブル WHERE ユーザーID = '{$_SESSION['user_id']}';

もしログイン認証されたユーザーが、このセッション変数 $_SESSION['user_id'] を変えられるのであれば、ほかのユーザーの名前や住所を見ることができてしまいます。(いわゆる個人情報の漏洩です)

しかし、セッション変数はサーバー上に保存され、ログイン認証されたユーザーでもセッション変数を変えることは絶対にできないため安全です。

セッションID を守るための5つの対策

PHP セッションを使ったログイン認証は基本的には安全なのですが、攻撃者はあの手この手でセッションIDを盗もうとしますので注意も必要です。そこで、セッションIDを守るために最低限必要な対策を5つあげてみました。

必ず HTTPS を使う

これはセッションID に限った話ではありませんが、インターネットを通じて秘密の情報(パスワードやクレジットカード番号など)をやりとりする場合は、盗聴のリスクがあるため HTTPS が必須です。

今や、入力フォームがあって HTTPS ではない場合は、ブラウザが警告を表示するようになりました。また、HTTPS を設定するのに必要なサーバー証明書を無料で発行してくれる認証局(Let's Encrypt)などもありますので、Webサイトでは必ず HTTPS を使いましょう。

関連記事:Let's Encrypt サーバー証明書の取得と自動更新設定メモ

セッションIDに Secure 属性をつける

セッションIDのクッキーに、Secure 属性(session.cookie_secure 初期値:off)をつけることで、HTTPS の場合にのみセッションIDを発行するようにできます。上の HTTPS とあわせて設定しましょう。

sudo vim /etc/php.ini

;session.cookie_secure =
 ↓
session.cookie_secure = 1

セッションIDに HttpOnly 属性をつける

セッションIDのクッキーに、HttpOnly 属性(session.cookie_httponly 初期値:off)をつけることで、セッションIDに JavaScript からアクセスできないように設定できるため、JavaScript を使って セッションID を盗まれるリスクを軽減できます。

sudo vim /etc/php.ini

session.cookie_httponly =
 ↓
session.cookie_httponly = 1

セッションに関する設定を変更しない

最近の PHP をパッケージからインストールしていれば、初期状態でセッションに関する設定は安全に設定されていますので、上記以外のセッションに関する設定は変更しないことをオススメします。

もし、設定を変更する必要がある場合は、PHPのマニュアル などでセッションIDが盗まれるリスクが発生しないことを、しっかり確認した上で変更しましょう。

以前とある Webサーバーの公開ディレクトリにセッションファイル(ファイル名がセッションID)が保存されていたことがありました(^^;) (セッションファイルの保存先は session_save_path 関数でプログラム側から簡単に変更できます)なにか事情があるにせよ。公開ディレクトリにセッションファイルを保存するようなことは、絶対にあってはなりません。

Webサーバーのセキュリティ対策をしっかり行う

セッションファイルが保存されている Webサーバーに不正アクセスされては、セッションIDは盗み放題ですし、セッションファイルを改ざんすることすらできてしまいます。Webサーバーのセキュリティ対策もしっかり行っておきましょう。

関連記事:これだけはやっておこう Linuxサーバのセキュリティ対策5つ

おわりに

PHP のセッションを使ったログイン認証は、簡単なコードで実装できるので大丈夫なのかと心配になりましたが、コードがシンプルであればそれだけプログラムミスが発生する可能性もぐっと少なくなります。PHP のセッションは、実にうまくできた仕組みですね。

コメント

タイトルとURLをコピーしました