PHP入門 | form送信の基本 | セキュリティ対策の第一歩(XSS防御)

フォームを送信してサーバーで処理するということは、「サーバーサイドプログラミング」の第一歩を踏み出したあなたが、次に必ず行うべきことがあるということだ。それは何だろうか。それは「今作ったプログラムの穴をふさぐこと」である。

サーバーにプログラムを提供するということは、不特定多数の人がそのサーバーにアクセスし、そのプログラムが実行されるということである。つまり、サーバーにアクセスするすべての人に対して「そのプログラムが安全に動作する」ことを保証する義務が発生する。もしそのプログラムによって利用者に何らかの被害が発生すれば、その責任は作成した開発者にある。

もちろん、最初から「すべてのセキュリティ対策を立てろ」という意味ではない。しかし、少なくとも最低限の基本的な安全対策については、プログラムを作り始めたら最初に学ぶべきである。

ところで、今作ったサンプルプログラムには大きな「穴」が空いている。その穴を確認してみよう。ブラウザで前のページにアクセスし、次のように書いて送信してみる。

<script>alert("これが穴です!");</script>

送信すると画面にアラートウィンドウが表示される。最近のブラウザではこれを自動的に防ぎ、警告メッセージが表示される場合もある。入力フィールドに書いたJavaScriptスクリプトが、ページを読み込むときに実行されるため、このような現象が起きる。なぜこれが「穴」なのか。それは「どこの誰か分からない人がここにJavaScriptスクリプトを書き、ページを表示したときにそれを実行させられる」からである。

例えば、このような掲示板プログラムを作ったとしよう。すると、そこにアクセスした人が今と同じようにJavaScriptスクリプトをこっそり投稿する。次にこの掲示板にアクセスしたすべての人に表示されるとき、そのスクリプトが実行される。例えば、そのスクリプトに「ブラウザに保存されているCookie情報を取得して別のサイトへ送る」といった処理が書かれていたらどうなるだろうか。アクセスした人のCookie情報が見知らぬ誰かに盗まれる可能性がある。

いわゆるスプーフィングという犯罪は、このようにして発生する。このスクリプトを利用した方法は「クロスサイトスクリプティング(XSS)」と呼ばれ、サイト攻撃の基本中の基本として広く知られている。

では、この空いた「穴」をふさいでみよう。最初に行うべきセキュリティ対策は、実は意外に簡単である。テキストを画面に表示するecho文のスクリプトを、次のように書き直すだけでよい。

<?php
    echo htmlspecialchars($result);
?>

このhtmlspecialcharsという関数は、括弧内に書かれた値、つまり引数をチェックし、HTMLタグをすべてエスケープ文字に変換したものを返す。つまり、このように値を書き出すことで、<script>タグなどをすべて無効化し、動作しないようにできる。

フォームを使う場合には、セキュリティ対策に関する鉄則がある。それは「ユーザーから送られてきたデータを絶対にそのまま画面へ出力しない」ということだ。そこにどのような内容が書かれているか分からないため、必ず「表示しても問題ない」形に処理してから出力することが、フォーム利用の基本である。

<?php
    $str = $_POST['text1'];
    if ($str != null){
        $result = "あなたは'{$str}'と書きました。";
    } else {
        $result = "何も書かれていません。";
    }
?>
<!DOCTYPE html>
<html lang="ko">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
        <title>sample page</title>
    </head>
    <body>
        <h1>Hello PHP!</h1>
        <div><?php
            echo  htmlspecialchars($result);
        ?></div>
        <form method="post" action="./index.php">
            <input type="text" name="text1">
            <input type="submit">
        </form>
    </body>
</html>