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での文字列の比較では
==
、!=
は使ってはいけません。
参考
Date: 2014/11/11