Laravel 4のCSRF脆弱性は何が問題だったのか?

PHPでのある意味「典型的な脆弱性」だったので記事を書くことにしました。

Laravel 4のCSRF脆弱性とは?

Laravel 4.2.10以前にCSRF保護が無効になる脆弱性が報告されました。

この脆弱性は、Laravel標準のCSRF保護(csrfフィルタ)を簡単に無効化することができるものです。

既存サイトでは、今すぐ、以下の修正パッチを摘要する必要があります。Laravelのアップデートでは修正されません。

From ba0cf2a1c9280e99d39aad5d4d686d554941eea1 Mon Sep 17 00:00:00 2001
From: Taylor Otwell <taylorotwell@gmail.com>
Date: Sun, 9 Nov 2014 16:29:56 -0600
Subject: [PATCH] Check type of token as well.

---
 app/filters.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/filters.php b/app/filters.php
index fd0b4bc..97a9468 100644
--- a/app/filters.php
+++ b/app/filters.php
@@ -83,7 +83,7 @@

 Route::filter('csrf', function()
 {
-   if (Session::token() != Input::get('_token'))
+   if (Session::token() !== Input::get('_token'))
    {
        throw new Illuminate\Session\TokenMismatchException;
    }

https://github.com/laravel/laravel/commit/ba0cf2a1c9280e99d39aad5d4d686d554941eea1.patch

新規にインストールしたLaravel 4.2.11以降では修正されています。

何が問題だったのか?

修正パッチを見れば、まっとうなPHPユーザの方はすぐにわかると思いますが、比較が!=でされていました。

このため、比較する値によっては自動型変換(キャスト)が起こり、意図せずこのチェックをすり抜けることが可能になっていた、ということです。

この点をよくご存じないという方は、以下などを参照して下さい。

でもGETやPOSTパラメータは文字列なのでは?

はい、PHPに詳しい方は、Input::get('_token')の返り値は文字列なのではないか?と疑問に思われたのではないかと思います。

Input::get()というメソッド名からは、$_GETへアクセスするメソッドかなと思うPHPユーザもいると思いますが、このメソッドはそんなに単純なものではありませんでした。

具体的な実装は、以下のようになっていました。

たぶん、ほとんどのLaravelユーザが、このInput::get()が文字列以外も返す可能性があることを知らなかったために、この脆弱性が長い間放置された原因なのではないかと想像します。

フィルタを定義するapp/filters.phpというファイルにあるコードが、ユーザの目に触れなかったということは考えにくいですから。

まとめ

  • PHPでの文字列の比較では==!=は使ってはいけません。

参考

Tags: php, csrf, security, laravel

スノーデン氏のアドバイスに従いDropboxではなくSpiderOakを使ってみます

Snowden Says Drop Dropbox, Use SpiderOak」ということで、SpiderOakを使うことにしてみました。

SpiderOak公式サイト

SpiderOakは、プライバシーを重視したDropboxみたいなオンラインストレージです。

  • Windows
  • Mac OS X
  • Linux
  • Android
  • iOS

で使えます。

‘Zero-Knowledge’ Privacyをキャッチコピーに、データは端末で暗号化されサーバ上で保管され、鍵をサーバで共有しないため、サーバ上ではファイルを見ることができません。

以下の紹介リンクから登録すると追加で1GBもらえて、無料で3GBになります。

ちなみに、DropboxのCEOの「反論」は「プライバシーは犠牲にするけど、他の利便性を提供するよ」みたいな、プライバシーの観点からは残念なものでした。

関連

Tags: linux, mac, storage

【訂正】PHPでのUserAgentパーサ(ブラウザ判定)ライブラリのベンチマーク

PHPでのUserAgentパーサ(ブラウザ判定)ライブラリは何がいいかベンチマークしてみたへの補足および訂正です。

ベンチマーク環境

  • XAMPP 1.8.3-4 for Linux (32bit)
    • PHP 5.5.11
      • Zend OPcache v7.0.4-dev

ベンチマーク結果

1つのUserAgent文字列からブラウザの種類などを判定する処理についてのベンチマークですが、処理する文字列により処理時間がかなり変わるようです。

以下のグラフは左が前回のもの、右が今回の別の文字列のものです。

timeベンチマークのグラフ timeベンチマークのグラフ

前回はCrossjoin\Browscapが非常に速かったのですが、今回はbrowscap-phpとそれほど違いはありません。

というわけで、1つだけでなく425個の異なるUserAgent文字列を処理した結果は以下のようになりました。なお、get_browser()関数はあまりに遅いので除外しました。

処理時間:
timeベンチマークのグラフ なんとCrossjoin\Browscapがbrowscap-phpより遅くなってしまいました。

最大使用メモリ:
memoryベンチマークのグラフ メモリについてはbrowscap-phpがもっとも消費することは変わりませんでした。

実際の数値:

{
  "crossjoin-browscap":{"time":27.748618125916,  "memory":1572864},
  "browscap-php":      {"time":19.118434906006,  "memory":33030144},
  "ua-parser":         {"time":0.38058090209961, "memory":786432},
  "woothee":           {"time":0.032510995864868,"memory":786432}
}

結論

Crossjoin\Browscapとbrowscap-phpの処理速度は状況(プラットフォームや処理するUserAgent文字列)により結果が変わります。Macでもやってみたのですが、その場合は、Crossjoin\Browscapが2倍以上速かったです。ただし、Crossjoin\Browscapの方がメモリ消費が少ないのは変わらないようです。

woothee-phpは安定して速く、メモリ消費も少ないようです。

ベンチマークはできるだけ実際の環境に近い条件で実施しましょう。

Tags: php, benchmark