用了半年的Laravel揍堕,越來越喜歡他了喉恋,他大量的設(shè)計都和我的觀念相互印證,而且往往更加精妙和通用梭域。今天就提筆記一個關(guān)于CSRF防護的問題。
我以前的方案
在我之前設(shè)計和使用的系統(tǒng)中搅轿,為了避免有人不小心寫出CSRF漏洞病涨,我會全局禁止$_REQUEST的使用以及混用POST和GET的行為,要求開發(fā)人員分清每個傳入數(shù)據(jù)是POST還是GET璧坟。
于是我會在Reqeust對象上增加get($field_name, $default), post($field_name, $default) 的方法既穆。并且直接在框架代碼啟動的時候?qū)⑷肿兞?_REQUEST置為空數(shù)組赎懦。
Laravel的方案
Part 1 Global CSRF Protection
Laravel推薦在全局注冊VerifyCsrfToken的Middleware,對所有Post,Put,Delete請求自動校驗是否帶合法的_csrf token幻工。而要在表單中添加這個Token励两,只需要在form中加一行:
<?php echo csrf_field(); ?>
獲取表單值的方法:
$request->input('name', 'default name');
Part 2 路由層面區(qū)分Post/Get
// 同個Path下的Get,Post請求會根據(jù)配置不同進入不同的處理邏輯
Route::get('/path', 'XXXController@func_get');
Route::post('/path', 'XXXController@func_post');
我覺得Laravel這么設(shè)計比我高明在兩個地方:
對于新人囊颅,如果在Laravel中新增了一個表單伐蒋,Post發(fā)現(xiàn)提交的時候提示CSRF校驗失敗,他很容易知道有這么個校驗迁酸,且開發(fā)環(huán)境下可以友好的提示他如何搞定這個問題先鱼。
而我的做法由于和PHP原生行為不一致,第一次碰到的人會很奇怪奸鬓,就算花時間最終搞明白了焙畔,他也會覺得自己掉了個坑,因為我沒有任何提示串远。(其實現(xiàn)在想來是可以有提示的宏多,應(yīng)該將$_REQUEST改成一個每次調(diào)用都拋Exception給予提示的閉包)。Laravel可以方便的配置例外情況澡罚。本周即將發(fā)布的5.1-LTS版包涵一個我很期待的改進就是可以通過配置伸但,忽略特定路徑下的CSRF校驗,以便兼容第三方代碼組件和第三方的Post數(shù)據(jù)回傳留搔。詳見
依然可能有的問題
我之前的項目中更胖,到后來,會發(fā)現(xiàn)有人寫這樣的代碼:
$val = $request->get('name', $request->post('name', 'defailt name'));
這么寫有時候是真的需要隔显;但大多數(shù)時候只是因為懶/或者copy來的代碼自己也沒搞明白却妨。
在Laravel中也無法完全避免這類問題,因為路由配置的時候除了Rouet::get() Route::post() 還有個大殺器 Route::any()括眠,它同時兼容get和post請求彪标,間接引入了同樣的問題。
遺憾
雖然在Laravel中掷豺,上述問題我可以增加UnitTest捞烟,檢查大家對any()的使用,但實在不夠優(yōu)雅当船,誰有更好的思路题画,非常希望你能來信告訴我 icedfish@gmail.com。