1. 程式人生 > >laravel CSRF 保護

laravel CSRF 保護

asc push -c form 邏輯 ros rap 存儲 請求方式

在開始之前讓我們來實現上述表單訪問偽造的完整示例,為簡單起見,我們在路由閉包中實現所有業務代碼:

Route::get(‘task/{id}/delete‘, function ($id) {
    return ‘<form method="post" action="‘ . route(‘task.delete‘, [$id]) . ‘">
                <input type="hidden" name="_method" value="DELETE"> 
                <button type="submit">刪除任務</button>
            </form>‘;
});

Route::delete(‘task/{id}‘, function ($id) {
    return ‘Delete Task ‘ . $id;
})->name(‘task.delete‘);

http://blog.test/task/1/delete 點擊「刪除任務」按鈕提交表單,會顯示 419 異常頁面:

技術分享圖片

初學者可能會困惑,這是什麽原因呢?

不得不說,Laravel 5.7 引入的錯誤提示頁面雖然好看,但是錯誤提示信息太少,這其實是因為默認情況下,為了安全考慮,Laravel 期望所有路由都是「只讀」操作的(對應請求方式是 GET、HEAD、OPTIONS),如果路由執行的是「寫入」操作(對應請求方式是 POST、PUT、PATCH、DELETE),則需要傳入一個隱藏的 Token 字段(_token)以避免[跨站請求偽造攻擊](CSRF)。在我們上面的示例中,請求方式是 DELETE,但是並沒有傳遞 _token

字段,所以會出現異常。

註:跨站請求偽造是一種通過偽裝授權用戶的請求來攻擊授信網站的惡意漏洞,關於跨站請求偽造攻擊可以參考維基百科了解明細:https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%AB%99%E8%AF%B7%E6%B1%82%E4%BC%AA%E9%80%A0。

避免跨站請求偽造攻擊的措施就是對寫入操作采用非 GET 方式請求,同時在請求數據中添加校驗 Token 字段,Laravel 也是這麽做的,這個 Token 值會在渲染表單頁面時通過 Session 生成,然後傳入頁面,在每次提交表單時帶上這個 Token 值即可實現安全寫入,因為第三方站點是不可能拿到這個 Token 值的,所以由第三方站點提交的請求會被拒絕,從而避免 CSRF 攻擊。

在 Laravel 中,和表單方法偽造一樣,支持通過 HTML 表單隱藏字段傳遞這個值:

Route::get(‘task/{id}/delete‘, function ($id) {
    return ‘<form method="post" action="‘ . route(‘task.delete‘, [$id]) . ‘">
                <input type="hidden" name="_method" value="DELETE"> 
                <input type="hidden" name="_token" value="‘ . csrf_token() . ‘">
                <button type="submit">刪除任務</button>
            </form>‘;
});

這樣我們再次訪問 http://blog.test/task/1/delete 頁面點擊「刪除任務」按鈕,即可成功提交表單。

當然,如果你是在 JavaScript 腳本中執行 HTTP 請求,也可以很方便的傳遞這個 Token 值執行寫入操作,首先需要在 HTML <head> 標簽內新增一個 <meta> 元素來存儲 Token 值:

<meta name="csrf-token" content="<?php echo csrf_token(); ?>" id="csrf-token">

然後我們在 JavaScript 腳本中將這個 Token 值放到一個全局請求頭設置中,以便每個 HTTP 請求都會帶上這個頭信息,避免每次發起請求都要添加這個字段。如果你使用的是 jQuery 的話,可以這麽做:

$.ajaxSetup({
    headers: {
        ‘X-CSRF-TOKEN‘: $(‘meta[name="csrf-token"]‘).attr(‘content‘)
    } 
});

如果你使用的是 Vue 的話,可以這麽做:

Vue.http.interceptors.push((request, next) => {
    request.headers[‘X-CSRF-TOKEN‘] = document.querySelector(‘#csrf-token‘).getAttribute(‘content‘);
    next();
});

Laravel 會在每次請求都檢查請求頭中是否包含 X-CSRF-TOKEN,並檢查其值是否和 Session 中的 Token 值是否一致。

註:如果你使用了 Laravel 自帶的 assets/js/bootstrap.js, 則上述 Vue 請求頭設置不需要自己編寫,因為 bootstrap.js 中已經包含了這個邏輯。

laravel CSRF 保護