PHP初心者がXSSを防ぐためにやること

# この記事を読むのがオススメな人
・PHP初心者
・XSSについて知りたい人
・簡易的な掲示板を作ってみたい人
# はじめに
最近PHPの勉強を初めたので、学んだことを備忘録としてまとめていきます。

# XSSとは？
XSS(クロスサイトスクリプティング)とは、簡単に言えば
 **他人のWebページに勝手にスクリプトを埋め込んで、閲覧者をだます攻撃のこと** です。
もう少し詳しく述べると、ウェブサイトの何かしらの入力欄に、悪い人がこっそりスクリプトを書き、それを見た人のブラウザで勝手にその命令がプログラムとして実行されてしまうことです。
では、このようなXSS攻撃を防ぐにはどうすればよいのでしょうか？

# XSS防止のためにhtmlspecialchars関数を使う
echoで出力する際にhtmlspecialchars関数を使えば良いのです。
htmlspecialchars関数は、PHPの関数の一つです。これを使うことによって、特殊文字を無効化することができます。特殊文字を無効化することによって、特殊文字が別の文字に置き換わるので、悪いスクリプトが機能しなくなります。

今回は簡易的な掲示板を作ったので、それを元にXSSの危険性とhtmlspecialchars関数の便利さを知ることができれば良いと思っています。

以下のphpファイルは、私が作った簡易的な掲示板です。仮に実行するならばcomment.csvも必要になります(ファイルの読み込みや書き込みのため！)。
comment.csv は書き込み先のファイルで、最初は空の状態（中身なし）で構いません。PHPスクリプトが自動的に追記してくれます。

```php:bbs.php
<!DOCTYPE html>
<html lang = "ja">
<head>
    <meta charset = "UTF-8">
    <title>簡易掲示板</title>
    
    <style>
        body {
            font-family: "Hiragino Kaku Gothic ProN", "メイリオ", sans-serif;
            background-color: #f0f2f5;
            margin: 30px;
            color: #333;
        }
        
        h1 {
            font-size: 26px;
            color: #555555;
            margin-bottom: 20px;
            text-align: center;
        }
        
        form {
            padding: 15px;
            border-radius: 10px;
            margin-bottom: 30px;
            text-align: center;
        }
        
        input[type = "text"] {
            padding: 8px;
            margin: 5px;
            width: 200px;
            border: 2px solid #b388eb;
            border-radius: 8px;
            font-size: 16px;
        }
        
        input[type = "submit"] {
            padding: 8px 20px;
            background-color: #b388eb;
            color: white;
            border: none;
            border-radius: 8px;
            font-size: 16px;
            cursor: pointer;
        }
        
        input[type="submit"]:hover {
            background-color: #9575cd;
        }
        
        table {
            width: 100%;
            border-collapse: separate;
            border-spacing: 0;
            margin-top 20px;
        }
        
        th {
            background-color: #e0d7f5;
            color: #6a1b9a;
            padding: 10px;
            border-bottom: 3px solid #f48fb1;
            text-align: center;
            font-size: 18px;
        }
        
        td {
            padding: 12px;
            border-bottom: 1px dashed #d1b3ff;
            background-color: #f6f0fc;
            font-size: 16px;
        }
        
        tr:nth-child(even) td {
            background-color: #f3e8ff;
        }
        
        .center {
            text-align: center;
        }
        
    </style>
    
</head>


<body>
    <h1>簡易掲示板</h1>
    <form action = "" method ="post">
        <input type = "text" name = "name" placeholder = "名前">
        <input type = "text" name = "comment" placeholder = "ここにコメントを入力">
        <input type = "submit" name = "submit" value = "送信">
    </form>
    <?php
    //使用するテキストファイル名を用意
    $filename = "comment.csv";
    
    if(isset($_POST ["submit"])) {
        $name = $_POST["name"];
        $comment = $_POST["comment"];
        $date = date("Y/m/d H:i:s"); //日時
        
        if(!empty($name) && !empty($comment)) {
            //投稿番号の取得(ファイルの行数 + 1)
            $post_number = 1;
            if(file_exists($filename)) {
                $lines = file($filename, FILE_IGNORE_NEW_LINES);
                $post_number = count($lines) + 1;
            }
            
            //データをちゃんと並べる
            $data = [$post_number, $name, $comment, $date];
            
            
            //書き込み(追記)
            $fp = fopen($filename, "a");
            fputcsv($fp, $data);
            fclose($fp);

        }
        
    }
    
    //データの読み取り
    $fp = fopen($filename, "r");
    
    echo'<table> 
            <tr>
            <th>投稿順</th>
            <th>名前</th>
            <th>コメント</th>
            <th>日付</th>
        </tr>'; 
    
    while($data = fgetcsv($fp)) {
    //テーブルセルに配列の値を格納
    //htmlspecialcharsはXSS防止・特殊文字無効化のために書いた
        echo '<tr>';
        echo '<td>' . htmlspecialchars($data[0], ENT_QUOTES) . '</td>';  //投稿番号
        echo '<td>' . htmlspecialchars($data[1], ENT_QUOTES) . '</td>';  //名前
        echo '<td>' . htmlspecialchars($data[2], ENT_QUOTES) . '</td>';  //コメント
        echo '<td>' . htmlspecialchars($data[3], ENT_QUOTES) . '</td>';  //日時
        echo '</tr>';
    }

    echo '</table>';

    fclose($fp);

    ?>
    
</body>
</html>

```
# 動かしてみる
とりあえず、まずはhtmlspecialcharsをつけたまま動かしてみましょう。
何か書き込んでみます。
名前の欄にエジソン、コメント欄にあの名言を書きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4068148/0552f8e2-9e2f-48e5-85f2-044ab0c3cbab.png)

ちゃんと動きましたね！
では、このまま悪意のあるスクリプトをコメント欄に埋め込んでみます。
```php
<script>alert("今からあなたのパソコンを使えなくしちゃうぞ")</script>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4068148/0701b986-5607-49d7-a0c3-d2978fb27f90.png)
このコードはアラートを表示するためのものです。
ですが何も起きません。ただ「今からあなたのパソコンを使えなくしちゃうぞ」というコメントが書き込まれただけですね。

では、以下のこの部分を
```php
echo '<td>' . htmlspecialchars($data[0], ENT_QUOTES) . '</td>';  //投稿番号
echo '<td>' . htmlspecialchars($data[1], ENT_QUOTES) . '</td>';  //名前
echo '<td>' . htmlspecialchars($data[2], ENT_QUOTES) . '</td>';  //コメント
echo '<td>' . htmlspecialchars($data[3], ENT_QUOTES) . '</td>';  //日時
```
htmlspecialcharsを無くしてこう書きます。
ENT_QUOTESは、シングルクオートとダブルクオートを変換するという意味です。
```php
echo '<td>' . $data[0] . '</td>';  //投稿番号
echo '<td>' . $data[1] . '</td>';  //名前
echo '<td>' . $data[2] . '</td>';  //コメント
echo '<td>' . $data[3] . '</td>';  //日時
```
これに差し替えてコードを実行した後に、前に入力した、
```php
<script>alert("今からあなたのパソコンを使えなくしちゃうぞ")</script> 
```
これをコメント欄に入力してみたいと思います。

すると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4068148/dd95b368-b150-4496-8d41-1733ba7e83fb.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4068148/e49ff648-61e4-4d7a-a38c-06bfae8ecc3a.png)
攻撃されてしまいました…(´;ω;｀)
というのは冗談で、攻撃されてしまったように見えますが、これは本当に危険な攻撃ではなく、あくまでJavaScriptのアラートが動いただけです。ですが、もしこれがCookieを盗むようなスクリプトだったら…と考えると怖いですね。
こんなことが起きないように、Webページの全ての出力に対してエスケープ処理を施すと良いかもしれないと思いました。

# おわりに

今回は、簡易掲示板を題材にXSSの危険性と、その対策としての `htmlspecialchars` 関数の重要性について学びました。
たった1行の関数を使うだけで、スクリプトによる攻撃を防ぐことができるということは、PHP初心者にとっても非常に大事な気づきだと思います。

読んでいただき、ありがとうございました！