LoginSignup
19
18

More than 5 years have passed since last update.

PHPでCSRF対策の話

Last updated at Posted at 2017-10-31

最近PHPを使う機会がありセキュリティについてすこーし気にするようになったので調べてみました。
PHPでCSRF(クロスサイトリクエストフォージェリ)について考えます。

CSRFとは

攻撃者がウェブページを作成し、訪れたユーザに対してニセのリンクを踏ませ、気づかぬうちに別のサイトへ書き込みを行わせる等の攻撃手法です。

一般化すると、サイトをまたがって(クロスサイト)偽(forgery)リクエストを送信する攻撃手法です。

Aというサイトに書き込もうとしていたのに、実は全然違うBというサイトに書き込まされていたってことが起こり得るのです。

ざっくり図解

csrf.png

対策

フォーム生成時にサーバからの認証用のtokenを埋め込み、リクエスト送信時に一緒にサーバに返し、正しいtokenが送られてきているか検証を行う。

実装

有名なフレームワークの実装探してパクれば問題無いはず。

今回はLaravelの実装を参考にしました。

_token という名前の値がセットされるようです。
https://github.com/illuminate/html/blob/master/FormBuilder.php#L179

    /**
     * Generate a hidden field with the current CSRF token.
     *
     * @return string
     */
    public function token()
    {
        $token = ! empty($this->csrfToken) ? $this->csrfToken : $this->session->getToken();
        return $this->hidden('_token', $token);
    }
<input name="_token" type="hidden" value="QiO0aQwyhcqKBVOUSkeOLlEQaDFay1ICTXGJ0Uh5">

生成はこの辺で行っているみたいです。
https://github.com/illuminate/session/blob/master/Store.php#L598

    /**
     * Regenerate the CSRF token value.
     *
     * @return void
     */
    public function regenerateToken()
    {
        $this->put('_token', Str::random(40));
    }

Strクラスのrandom関数とかphpにはないので独自拡張を行っているみたいです。
https://github.com/illuminate/support/blob/master/Str.php#L285

    /**
     * Generate a more truly "random" alpha-numeric string.
     *
     * @param  int  $length
     * @return string
     */
    public static function random($length = 16)
    {
        $string = '';
        while (($len = strlen($string)) < $length) {
            $size = $length - $len;
            $bytes = random_bytes($size);
            $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
        }
        return $string;
    }

PHPの標準(7.0〜)の random_bytes を利用してbase64エンコードかけて /, +, = を除外する感じになっているようです。

PHP7未満で5.3以上であれば openssl_random_pseudo_bytes を利用するといいみたいです
(出典: teratail-ベストアンサー)
http://php.net/manual/ja/function.openssl-random-pseudo-bytes.php

よくわからないので知ってる方いたら教えてください

日本語で書かれた記事だとセッションID使ってる例が多い気がするんですが(観測範囲の問題???)、セッションIDをハッシュ化したらtokenとして使っていいのだろうか?ランダムじゃなくていいの?

例を出すとこういうやつ

<input type="hidden" name="token" value="<?=sha1(session_id())?>">

まとめ

すこしはセキュリティ意識してコード書こう!
人類の英知が詰まったフレームワークの実装読むのいいよ!

19
18
2

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
19
18