跳到內容

CSRF 保護

簡介

跨站請求偽造是一種惡意攻擊,未經授權的指令會以已驗證的使用者身分執行。 幸好,Laravel 讓保護您的應用程式免於遭受跨站請求偽造 (CSRF) 攻擊變得容易。

漏洞說明

如果您不熟悉跨站請求偽造,讓我們討論一個如何利用此漏洞的範例。 想像一下您的應用程式有一個 /user/email 路由,它接受 POST 請求來變更已驗證使用者的電子郵件地址。 最有可能的是,此路由預期 email 輸入欄位包含使用者想要開始使用的電子郵件地址。

如果沒有 CSRF 保護,惡意網站可能會建立一個 HTML 表單,指向您應用程式的 /user/email 路由並提交惡意使用者自己的電子郵件地址

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>

如果惡意網站在頁面載入時自動提交表單,則惡意使用者只需要誘騙您應用程式的不疑心的使用者訪問他們的網站,他們的電子郵件地址將在您的應用程式中被更改。

為了防止此漏洞,我們需要檢查每個傳入的 POSTPUTPATCHDELETE 請求,以尋找惡意應用程式無法存取的秘密 session 值。

防止 CSRF 請求

Laravel 會為應用程式管理的每個活動使用者 session 自動產生一個 CSRF「token」。 此 token 用於驗證已驗證的使用者是否為實際向應用程式發出請求的人。 由於此 token 儲存在使用者的 session 中,並且每次 session 重新產生時都會變更,因此惡意應用程式無法存取它。

目前 session 的 CSRF token 可以透過請求的 session 或透過 csrf_token 輔助函數存取

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 保護中介層可以驗證請求。 為了方便起見,您可以使用 @csrf Blade 指令來產生隱藏的 token 輸入欄位

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

預設包含在 web 中介層群組中的 Illuminate\Foundation\Http\Middleware\ValidateCsrfToken 中介層將自動驗證請求輸入中的 token 是否與儲存在 session 中的 token 相符。 當這兩個 token 相符時,我們就知道已驗證的使用者是發起請求的人。

CSRF Token 與 SPA

如果您正在建構一個將 Laravel 用作 API 後端的 SPA,您應該查閱 Laravel Sanctum 文件,以取得有關使用您的 API 進行身份驗證和防止 CSRF 漏洞的資訊。

從 CSRF 保護中排除 URI

有時您可能希望從 CSRF 保護中排除一組 URI。 例如,如果您使用 Stripe 處理付款並使用他們的 webhook 系統,您將需要從 CSRF 保護中排除您的 Stripe webhook 處理常式路由,因為 Stripe 不會知道要將哪個 CSRF token 發送到您的路由。

通常,您應該將這些路由放在 Laravel 應用於 routes/web.php 檔案中所有路由的 web 中介層群組之外。 但是,您也可以透過將其 URI 提供給應用程式 bootstrap/app.php 檔案中的 validateCsrfTokens 方法來排除特定路由

1->withMiddleware(function (Middleware $middleware) {
2 $middleware->validateCsrfTokens(except: [
3 'stripe/*',
4 'http://example.com/foo/bar',
5 'http://example.com/foo/*',
6 ]);
7})

為了方便起見,當執行測試時,CSRF 中介層會自動為所有路由停用。

X-CSRF-TOKEN

除了檢查 CSRF token 作為 POST 參數之外,預設包含在 web 中介層群組中的 Illuminate\Foundation\Http\Middleware\ValidateCsrfToken 中介層,也會檢查 X-CSRF-TOKEN 請求標頭。 例如,您可以將 token 儲存在 HTML meta 標籤中

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

然後,您可以指示像 jQuery 這樣的函式庫自動將 token 新增到所有請求標頭。 這為使用舊版 JavaScript 技術的 AJAX 應用程式提供了簡單、方便的 CSRF 保護

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

X-XSRF-TOKEN

Laravel 將目前的 CSRF token 儲存在加密的 XSRF-TOKEN Cookie 中,該 Cookie 包含在框架產生的每個回應中。 您可以使用 Cookie 值來設定 X-XSRF-TOKEN 請求標頭。

此 Cookie 主要作為開發人員的便利性而發送,因為某些 JavaScript 框架和函式庫(如 Angular 和 Axios)會自動將其值放在同源請求的 X-XSRF-TOKEN 標頭中。

預設情況下,resources/js/bootstrap.js 檔案包含 Axios HTTP 函式庫,它將自動為您發送 X-XSRF-TOKEN 標頭。

Laravel 是最有效率的方式來
建構、部署和監控軟體。