CSRF対策としてのトークンについて
お問い合わせフォーム送信時のCSRF対策について調べました。
おもにPHPでの実装の時用。
といってもPHPにかぎらずですが。
結論
- お問い合わせフォームにはワンタイムトークンでよい。
- ベースとなる文字列は擬似乱数生成系が良い(基本的にはセッションIDを使えば良い)
- PHPの場合は5.3以上ならopenssl_random_pseudo_bytes使っても良い。
- ちょっと心配ならセッションIDをベースにSHA256でハッシュ化を1回といったところでしょうか。
PHPでのワンタイムトークンの実装例
redwarcueidのおぼえがき(´_ゝ`): ワンタイムトークンの生成とチェック
トークンの半券とオリジナルを生成して、送信時に照合してチェックする方式。
強度は高そうですし。このままコピペしたら使えるという素晴らしいサンプルでこれを導入してる人は多そう。ありがたいです。
ここからは思考タイムですが
今回のお問い合わせフォーム送信時にそのまま使うかなーと考えた時
- そこまで何度もストレッチングしてサーバに負荷を与える必要があるのか?
- ハッシュ化の基になる値は擬似乱数生成系するのが良いとある
他の例を見てみる
高木浩光@自宅の日記 - CSRF対策に「ワンタイムトークン」方式を推奨しない理由, hiddenパラメタは漏れやすいのか?
乱数を生成するということはそう容易いことではない。自動的にセキュアであると謳うCSRF対策をするならば、チェック用の値は暗号学的に安全な擬似乱数生成系で生成しなければならない。
このような著しく特殊な脆弱性が将来生ずる可能性があると心配するならば、「セッションIDをそのままhiddenに入れないほうがよい」という理屈は成り立つ。
CSRF対策のトークンをワンタイムにしたら意図に反して脆弱になった実装例 - 徳丸浩の日記(2011-01-27)
co3k.org - Blog - 攻撃者がセッション ID を入手可能な状態で CSRF 攻撃を仕掛ける可能性について検討した
推測ですが、セッション ID が GET パラメータとかに含まれたりする可能性を避けたかったんじゃないかなあとか思います(フォームは GET でも使われうることを想定している)。 GET パラメータにセッション ID のような種類の情報は含めるべきではありません。
セッション ID と CSRF 対策用トークンを別の値にするということにはそれなりの優位性は存在すると思います。ただ、それでも個人的にはセッション ID で充分だと思いますが。
CSRF の安全なトークンの作成方法 | Webセキュリティの小部屋
■暗号論的擬似乱数生成器による乱数でトークンを作成する
PHP では以下のものを使用することができます。PHP: openssl_random_pseudo_bytes (>=PHP5.3.0)
■トークンの長さは32桁
CSRF のトークンの強度は、セッション ID と同程度でよいので、20桁以上あればよいと思いますが、いくつかの実装のセッション ID の桁数を見てみましょう。
Tomcat: 32桁
ASP.NET: 24桁
PHP: 26桁
この状況から、トークンが32桁あれば十分な強度であると言えます。