はじめに
新規で担当したシステムに脆弱性が検出されたため、その対応策をまとめました。
◆不具合の内容
ユーザー機能を持つシステムにおいて、ユーザーIDのパラメータを変更してアクセスすると、ログイン中のユーザー以外の情報が表示される不具合が発生しました。
◆使用言語
JavaScript
PHP
◆改善策
ログイン時にユーザーIDのパラメータをセッションに保存し、他のページにアクセスした際にパラメータとの整合性をチェックすることで、意図しない情報が表示されないようにします。
◆手順
1.ログインボタンの押下時に、JavaScriptでPHPにリクエストを送信
ログインページにユーザーIDを入力するテキストボックスが用意されている前提で進めます。
function login() {
var userId = document.getElementById('userId');
var data = 'userId=' + encodeURIComponent(userId.value);
xhr = new XMLHttpRequest();
xhr.open('POST', login.php , true);
xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' );
xhr.send(data);
}
2.PHPでリクエストパラメータをセッションに保存
<?php
// セッション開始
session_start();
// userIdがPOSTされていて、まだセッションにloginIdが設定されていない場合
if (!empty($_POST['userId']) && empty($_SESSION['loginId'])) {
$_SESSION['loginId'] = $_POST['userId'];
exit;
}
?>
3.保存したセッションIDとリクエストパラメータを照合
他のページへ遷移するボタンなどを押した際に、JavaScriptからPHPに新しいリクエストパラメータを送信します。
function userInfo() {
var userId = document.getElementById('userId');
var data = 'userId=' + encodeURIComponent(userId.value);
xhr = new XMLHttpRequest();
xhr.open('POST', login.php , true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
// PHPからのレスポンスを格納する
var response = xhr.responseText;
if ("ResultChk\n-3\n" == response) {
alert("ユーザー情報が一致しません。");
xhr.abort();
xhr.onreadystatechange = function() {};
xhr = null;
// 強制ログアウト処理
logout();
return;
} else {
// 遷移後のページを表示する処理など
}
}
}
}
xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' );
xhr.send(data);
}
4.リクエストパラメータとセッションに保存されたパラメータの照合
login.phpにリクエストパラメータとセッションパラメータの照合処理を追加します。
<?php
// セッション開始
session_start();
// userIdがPOSTされていて、まだセッションにloginIdが設定されていない場合
if (!empty($_POST['userId']) && empty($_SESSION['loginId'])) {
$_SESSION['loginId'] = $_POST['userId'];
exit;
} else if (isset($_POST['userId']) && isset($_SESSION['loginId'])) {
// 他ページ遷移時のパラメータを$editIdに代入
$userId = $_POST['userId'];
// ログイン時に保存したパラメータを$loginIdに代入
$loginId = $_SESSION['loginId'];
// userIdとセッションのIdが一致しなければエラー文を返却
if ($userId !== $loginId) {
echo "ResultChk\n-3\n";
exit;
}
}
?>
これで、JSからPHPへのパラメータ送信が実装でき、ログインIDとリクエストパラメータが一致しない場合にエラーを返却できるようになります。
◆まとめ
今回のシステムはCGI(Perl)による処理が含まれていましたが、CGIに関する知識が浅く、画面描画の仕組みを理解するのに一苦労でした。
もともとCGI経由でXHRを送信するコードはあったのですが、CGIを通してPHPのセッションに値を持たせる方法が見つからず、最終的にJavaScriptから直接PHPにリクエストを送信する処理を追加することにしました。
クライアントとサーバーの関係性やセッションについての理解が深まり、プログラミングの楽しさをより実感できました。
XMLHttpRequestについての知識は別の記事でまとめられたらいいなと思っています。