徳丸さんとは論理的に議論をする余地がないので、議論をするつもりは全くありません。しかし、完全に間違った指摘をされているのでコメントしておきます。
問題のブログはこちらです。
書籍『Webアプリケーションセキュリティ対策入門』のCSRF脆弱性
トークンの有効範囲は?
トークンがDBに保存される場合、トークンの有効範囲が気になるところです。大垣本および第二版のソースを見ると、トークンを保存するテーブルの定義は以下の通りです。
012 CREATE TABLE form_id (sha1 TEXT PRIMARY KEY, created TEXT NOT NULL)sha1がトークン、createdが生成日時を保持します。
シンプルな構造ですが、これだとトークンは、ユーザーやセッションを超えて、アプリケーション全体で共通になっています。これはまずそうですね。
トークンをホテルの部屋の鍵に例えると、こうです。大垣方式の鍵は、ホテルの全ての部屋に共通で使える鍵です。本来は、鍵は特定の1部屋のみに使えるべきですが、そうなっていないのです
全くの誤りです。
脆弱だ、とされているプログラムはこちらです。
CSRFトークンとなるIDは以下のように生成されています。
0 1 2 3 4 5 6 7 8 9 10 11 12 | // FORM_IDを取得 public function CreateFormID() { // /dev/urandomなどのセキュアなRNG(乱数生成機)を利用した方が良い // $this->secretが安全ならこれでも安全 $sha1 = sha1($this->secret . mt_rand() . microtime()); $sql = "INSERT INTO form_id (sha1, created) VALUES (". $this->quote($sha1) .", ". $this->quote(time()) .");"; if (!$this->Exec($sql)) { trigger_error('FORM_IDの作成に失敗しました。'); } return $sha1; } |
DBで共有されているどころか、完全にランダムになっています。つまり
- 誰とも共有していないランダムなキー
になっています。分かり易くいうとランダムキーであるセッションIDと同じです。このプログラムの問題を挙げるとすれば、
- コリージョンを考慮しなければならないハッシュ値にSHA1を使っていること(SHA1はNISTでは「コリージョン」(衝突)を考慮しなければならないハッシュは使用禁止)
- SHA1を使っているのにPHPのセッションモジュールのように衝突検出をしていないこと(私が実装した session.use_strict_mode のように衝突検知していないこと)
- 時間ベースと疑似乱数による弱いランダム化(これはコメントで安全な/dev/urandomを使う方が良いと記載)
です。古いプログラムなのでここまでの変更はしていません。
このサンプルの場合、CSRFトークンは”使い捨て”のランダムキーで一度使われると削除され、2度と使えません。固定のCSRFトークンに比べ格段にセキュアーです。
指摘は完全に誤りなので追記で誤っていた旨のみ記載していただけると助かります。
因みに、徳丸さん。参照されているツイッターユーザーですが、ISO 27000に明確に書いてあることを書いていないなどとツイートされていた事があるので注意された方が良いと思います。