最近PHPを使う機会がありセキュリティについてすこーし気にするようになったので調べてみました。
PHPでCSRF(クロスサイトリクエストフォージェリ)について考えます。
CSRFとは
攻撃者がウェブページを作成し、訪れたユーザに対してニセのリンクを踏ませ、気づかぬうちに別のサイトへ書き込みを行わせる等の攻撃手法です。
一般化すると、サイトをまたがって(クロスサイト)偽(forgery)リクエストを送信する攻撃手法です。
Aというサイトに書き込もうとしていたのに、実は全然違うBというサイトに書き込まされていたってことが起こり得るのです。
ざっくり図解
対策
フォーム生成時にサーバからの認証用の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())?>">
まとめ
すこしはセキュリティ意識してコード書こう!
人類の英知が詰まったフレームワークの実装読むのいいよ!