【PHP】お問い合わせフォームでセキュリティ対策

ふぐ

お問い合わせフォームでもその他フォームでも
セキュリティ対策しておかないといたずら攻撃されちゃうリスクがある

本記事で入れるセキュリティ対策

  • CSRFトークン対策
    画面が表示された時にワンタイムトークンを作成しておいて、
    お問い合わせ送信時にそのトークンが一致しているか確認する
  • 入力項目サニタイズ
    フォームで意図的に入力されたHTMLやプログラミングコードを無効にする

1. CSRFトークン対策

  • CSRFとは・・・
    クロスサイトリクエストフォージェリの略称 (長い…)
    リクエスト強要とも言われる

    Webサイトの脆弱性の1つ、もしくはそれを利用した攻撃のこと

    攻撃にはいろんな種類があるけど、例えば・・

    例① 問い合わせフォーム送信のURLに自動でPOSTするシェルを作って、ゴミ問い合わせを大量送信する
    例② 決済サービスで自分の口座に振り込み実行させるURLをフィッシングメールやサイトなどに仕込んでおいて、ログインユーザーがそれを踏んだ時に勝手に決済させる

    などなど

    機密的なデータを扱ったり、データの送信・更新・削除など重要な処理には
    CSRF対策を入れてあげる必要がある

  • 手順
  1. フォーム画面表示時にトークン (=推測されないランダムな文字列) を作成して
    セッションに保存する
    ( フォーム表示のたびに新しいトークンが作成されるのでワンタイムトークンという )
  2. 上記で作成したトークンを送信フォームにhiddenで持たせておく
  3. 送信時にセッションに保持されているトークンと、
    フォームから送られたトークンが一致しているかチェックする

これで、外部からリクエストがきた際にはトークン不一致として弾くことができる

  • ソース

form.php (トークン生成・セッション保存・フォームに保持)

<?php
// ⭐️ セッションを利用する
session_start();

// ⭐️ csrf用トークンを生成
$toke_byte = openssl_random_pseudo_bytes(16);
$csrf_token = bin2hex($toke_byte);
// ⭐️ トークンをセッションに保存
$_SESSION['csrf_token'] = $csrf_token;
?>
<form action="confirm.php" name="contact-form" method="post">
  <!-- ⭐️ 生成したトークンをhiddenで持たせる -->
  <input type="hidden" name="csrf_token" value="<?= $csrf_token ?>">
  <input type="text" name="email" placeholder="メールアドレス" />
  <button type="submit">送信</button>
</form>

send.php (トークンチェック)

// ⭐️ csrfトークンチェック
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
  // ⭐️ トークンチェックエラー時
  $_SESSION['error_message'] = 'お問い合わせの送信に失敗しました。';
  header('Location: //'.$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']). '/form.php');
  exit;
}

// ⭐️ トークンチェックOKなので送信処理 (省略)

入力項目サニタイズ

  • サニタイズとは
    日本語訳すると「消毒する」という意味
    フォームにHTMLやプログラミングコードが含まれていたら除去や無効化する処理

  • フォームにHTMLやプログラミングが含まれていると・・
    例えばフォームに下記が入力されていたとする
    <script> alert("こんにちは!"); </script>

    このデータが管理画面などで表示されると、その度にアラートが上がってしまう
    まだ可愛いいたずらだけど、これが不正なURLにリダイレクトする処理だったり情報を勝手に取得する処理だったりすると大変なことに・・。

// ⭐️ 入力値をサニタイズ
$content = htmlspecialchars($_POST['content'], ENT_QUOTES);