CSRF 保護

簡介

CSRF (跨網站要求偽造,Cross-site Request Forgery) 是一種在通過登入使用者來進行未授權操作的惡意入侵方式。還好,Laravel 能讓你輕鬆保護網站免於遭受 CSRF 攻擊。

CSRF 弱點說明

為避免讀者不熟悉 CSRF,我們來討論有關如何入侵該弱點的範例。假設專案中有個 /user/email 路由接受 POST 請求來修改登入使用者的 E-Mail 位址。顯然,這個路由預期有個 email 輸入欄位,其中包含了該使用者要使用的 E-Mail 位址。

若沒有 CSRF 保護,某個惡意網站可以建立一個 HTML 表單指向網站的 /user/email 路由,並送出假的使用者 E-Mail 位址:

1<form action="https://your-application.com/user/email" method="POST">
2 <input type="email" value="[email protected]">
3</form>
4 
5<script>
6 document.forms[0].submit();
7</script>
1<form action="https://your-application.com/user/email" method="POST">
2 <input type="email" value="[email protected]">
3</form>
4 
5<script>
6 document.forms[0].submit();
7</script>

若這個惡意網站在頁面載入後自動送出該表單,則惡意使用者只需要誘拐某個不經意的使用者瀏覽惡意網站,該使用者的 E-Mail 位址就會被修改。

為了防止此一弱點,我們需要在所有連入的 POST, PUT, PATCHDELETE 請求上檢查某個私密 Session 值,該 Session 值必須是惡意網站無法存取的。

防止 CSRF 請求

Laravel 會自動為每個有效的使用者 Session 產生一個由網站管理的 CSRF「權杖 (Token)」。該權杖會用來認證正在登入的使用者是否真的是實際發起該請求的使用者。由於該權杖儲存於使用者 Session 內,且會在每次 Session 重新產生的時候更改,因此惡意網站無法存取該權杖。

可以通過請求的 Session 或是 csrf_token 輔助函式存取目前 Session 的 CSRF 權杖:

1use Illuminate\Http\Request;
2 
3Route::get('/token', function (Request $request) {
4 $token = $request->session()->token();
5 
6 $token = csrf_token();
7 
8 // ...
9});
1use Illuminate\Http\Request;
2 
3Route::get('/token', function (Request $request) {
4 $token = $request->session()->token();
5 
6 $token = csrf_token();
7 
8 // ...
9});

定義 "POST", "PUT", "PATCH", 或是 "DELETE" 的 HTML 表單時,應在表單內包含一個隱藏的 CSRF _token 欄位以讓 CSRF 保護 Middleware 認證該請求。為了方便起見,可以使用 @csrf Blade 指示詞來產生這個隱藏的權杖輸入欄位:

1<form method="POST" action="/profile">
2 @csrf
3 
4 <!-- 同等於... -->
5 <input type="hidden" name="_token" value="{{ csrf_token() }}" />
6</form>
1<form method="POST" action="/profile">
2 @csrf
3 
4 <!-- 同等於... -->
5 <input type="hidden" name="_token" value="{{ csrf_token() }}" />
6</form>

預設包含在 web Middleware 群組內的 App\Http\Middleware\VerifyCsrfToken Middleware 會自動認證請求內的這個權杖是否符合儲存在 Session 內的權杖。若這兩個權杖相符,則我們就知道是登入使用者執行該請求的。

CSRF 權杖與 SPA

若正在建立使用 Laravel 作為 API 後端的 SPA,則可以考慮參考 Laravel Sanctum 說明文件瞭解有關使用 API 認證與保護 CSRF 弱點的資訊。

自 CSRF 保護內排除 URI

有時候,我們可能會想從 CSRF 保護內排除一些 URI。舉例來說,若正在使用 Stripe 來處理付款,並使用 Stripe 的 Webhook 系統,則需要將 Stripe Webhook 處理程式的路由從 CSRF 保護內排除,因為 Stripe 並不會知道要傳送什麼 CSRF 權杖給你的路由。

通常來說,應將這類路由放在 web Middleware 群組外。App\Providers\RouteServiceProvider 會將所有 routes/web.php 內的路由都套用到 web Middleware 群組內。不過,也可以通過將這些要排除的 URI 新增到 VerifyCsrfToken Middleware 內的 $except 屬性來排除這些路由:

1<?php
2 
3namespace App\Http\Middleware;
4 
5use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
6 
7class VerifyCsrfToken extends Middleware
8{
9 /**
10 * The URIs that should be excluded from CSRF verification.
11 *
12 * @var array
13 */
14 protected $except = [
15 'stripe/*',
16 'http://example.com/foo/bar',
17 'http://example.com/foo/*',
18 ];
19}
1<?php
2 
3namespace App\Http\Middleware;
4 
5use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
6 
7class VerifyCsrfToken extends Middleware
8{
9 /**
10 * The URIs that should be excluded from CSRF verification.
11 *
12 * @var array
13 */
14 protected $except = [
15 'stripe/*',
16 'http://example.com/foo/bar',
17 'http://example.com/foo/*',
18 ];
19}
lightbulb

為了方便起見,在執行測試時會自動禁用所有路由的 CSRF Middleware。

X-CSRF-TOKEN

除了使用 POST 參數來檢查 CSRF 權杖外,App\Http\Middleware\VerifyCsrfToken Middleware 也會檢查 X-CSRF-TOKEN 請求標頭。舉例來說,我們可以將該權杖儲存於 HTML meta 標籤內:

1<meta name="csrf-token" content="{{ csrf_token() }}">
1<meta name="csrf-token" content="{{ csrf_token() }}">

然後,可以讓如 jQuery 之類的函式庫自動將這個權杖加到所有請求標頭上。這樣就可為一些使用老舊 JavaScript 技術的 AJAX 程式提供簡單方便的 CSRF 保護:

1$.ajaxSetup({
2 headers: {
3 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
4 }
5});
1$.ajaxSetup({
2 headers: {
3 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
4 }
5});

X-XSRF-TOKEN

Laravel 將目前的 CSRF 權杖儲存為加密的 XSRF-TOKEN Cookie,會被包含在所有又框架產生的回應內。可以使用這個 Cookie 值來設定 X-XSRF-TOKEN 請求標頭。

由於一些 JavaScript 框架如 Angular 與 Axios 會自動在同源請求時將該 Cookie 的值放在 X-XSRF-TOKEN 標頭內,該 Cookie 就是為了提供開發者方便而傳送的。

lightbulb

預設情況下,resources/js/bootstrap.js 檔案已包含了 Axios HTTP 函式庫,該函式庫會自動為你傳送 X-XSRF-TOKEN 標頭。

翻譯進度
100% 已翻譯
更新時間:
2023年2月11日 上午10:27:00 [世界標準時間]
翻譯人員:
  • cornch
幫我們翻譯此頁

留言

尚無留言

“Laravel” is a Trademark of Taylor Otwell.
The source documentation is released under MIT license. See laravel/docs on GitHub for details.
The translated documentations are released under MIT license. See cornch/laravel-docs-l10n on GitHub for details.