437
450

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

とっても簡単なCSRF対策

Last updated at Posted at 2013-07-19

【2021/10/15 追記】
この記事は更新が停止されています。現在では筆者の思想が変化している面もありますので,過去の記事として参考程度にご覧ください。

CSRFおよびその対策の仕組みに関してはこちら↓

これで完璧!今さら振り返る CSRF 対策と同一オリジンポリシーの基礎 - Qiita

この記事は,PHPにおけるワンタイムトークンを用いた実装例を示すものです。執筆日が少々古いものになるのでご了承ください。

コメント欄の議論に関するまとめ

以下,XSS脆弱性が存在しない前提.この脆弱性があるとあらゆるCSRF対策がほとんど意味をなさなくなるので,まずここから潰しておくこと.

セッション固定攻撃に対する対策

  • ログイン後にsession_regenerate_idを必ず実行する.
  • ログアウト後にsession_destroyを必ず実行する.

CSRF攻撃に対する対策

  • セッションIDを抜かれることは原則的には無いが,セッションIDをそのままトークンに用いるのは避けたほうが無難.
  • ワンタイムトークンにはF5リロードでの誤作動を防止する意味合いもあるが,必ずしもワンタイム性が求められるわけではない.固定トークンでも十分なセキュリティは保証される.
  • トークンによる対策が施されていない場合,GETであってもPOSTであってもCSRFは実行可能なので,それは議論の対象ではない.

実装

以前はワンタイムトークン推奨にしていましたが,意向が変わってきたので固定トークンのサンプルに差し替えておきます.

クラス定義
class CsrfValidator {

    const HASH_ALGO = 'sha256';

    public static function generate()
    {
        if (session_status() === PHP_SESSION_NONE) {
            throw new \BadMethodCallException('Session is not active.');
        }
        return hash(self::HASH_ALGO, session_id());
    }

    public static function validate($token, $throw = false)
    {
        $success = self::generate() === $token;
        if (!$success && $throw) {
            throw new \RuntimeException('CSRF validation failed.', 400);
        }
        return $success;
    }

}

使い方

フォームに埋め込むとき
<input type="hidden" name="token" value="<?=CsrfValidator::generate()?>">
検証するとき(返り値チェックタイプ)
<?php

if (!CsrfValidator::validate(filter_input(INPUT_POST, 'token'))) {
    header('Content-Type: text/plain; charset=UTF-8', true, 400);
    die('CSRF validation failed.');
}

// 続きの処理

?>
<!DOCTYPE html>
...
検証するとき(例外タイプ)
<?php

try {

    CsrfValidator::validate(filter_input(INPUT_POST, 'token'), true);
    
    // 続きの処理…

} catch (\RuntimeException $e) {

    header('Content-Type: text/plain; charset=UTF-8', true, $e->getCode() ?: 500);
    die($e->getMessage());

}

?>
<!DOCTYPE html>
...
437
450
20

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
437
450

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?