很多時候我們對 request 內容要有比較複雜的檢查與過濾機制,例如說呼叫 API 的授權檢查,或是針對帶入資料是否含有 XSS 攻擊的可能性等諸如此類比較複雜性的檢查,這時候可以利用 Laravel 提供的 middleware 機制有效的達成需求。今天介紹的包含下列項目,我們會以 XSS 過濾為例,逐一說明。
app\Http\Middleware
底下產生 middleware。php artisan make:middleware <middleware 名稱>
handle()
是接下來要撰寫檢查邏輯的地方。<?php
namespace App\Http\Middleware;
use Closure;
class XSSFilterMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
return $next($request);
}
}
public function handle($request, Closure $next, $type)
{
$requestBody = $request->request->all();
$queryStrings = $request->query->all();
$requestXSSIsPossible = $this->checkXSS($requestBody, $type, $request);
$queryXSSIsPossible = $this->checkXSS($queryStrings, $type, $queryStrings);
if ($queryXSSIsPossible && $type === "encode") {
$newParamsBag = new ParameterBag($queryStrings);
$request->query = $newParamsBag;
}
if ($requestXSSIsPossible || $queryXSSIsPossible) {
switch ($type) {
default:
case "exception":
$responseData = [
"message" => "XSS警告: request 和 query string 資料不可包含 HTML 特殊字元"
];
// 拋出錯誤
throw new HttpResponseException(response()->json($responseData, 422));
case "pass":
case "encode":
break;
}
}
// 通過 middleware 檢查
return $next($request);
}
private function checkXSS(array $data, $type, &$target)
{
$result = false;
// ...
return $result;
}
可以看到 handle()
增加了一個 $type
的參數,根據官方文件的說明,若有需要帶入自定義的參數,只要加在 Closure $next
後面即可且不限數量。
如果 middleware 不合法,可以 Laravel route()
方法跳轉,或像範例中直接拋出 exception;當然也可以直接回傳一個 response 結果,這部份就跟 controller 的處理是一樣。
如果要通過 middleware 繼續向下執行,則回傳 $next($request)
。
完成 middleware 的邏輯之後,要先至 app\Http\Kernel.php
註冊。
若要讓所有 routes 都使用,只需在 $middleware
的陣列中加入 middleware 類別;若只要個別選用設定,則加到 $routeMiddleware
陣列底下。
protected $routeMiddleware = [
// ...
'xss' => \App\Http\Middleware\XSSFilterMiddleware::class,
];
結合昨天 route 說明,實際使用情形如下:
// 個別 route 設定:
Route::post('encode-test', function (Request $request) {
dd("通過");
})->middleware("xss:encode");
// 以群組的方式綁定:
Route::middleware("xss:exception")->group(function () {
Route::post('exception-text', function (Request $request) {
dd($request);
})
});
以 xss:exception
測試結果如下:
可以看到,因為 request 的 userId
包含 HTML 特殊字元,因此不會顯示「通過」訊息,而是顯示 「XSS警告...」 訊息。
以 xss:encode
測試結果如下:
在 $request
中的 json.request
(即 request
) 和 query
兩個屬性中,都帶有經過 encode 過後的結果。如此一來,後續 service 使用或是未來輸出給前端畫面使用的時候,就可以避免遭受到 XSS 的攻擊。
如果覺得要一個個 middlewares 加在每一個 routes 上面很麻煩,可以在 app\Http\Kernel.php
的 $middlewareGroups
中個別分組,在 route 中只要以分組名稱作為 key 即可使用,所以不要跟 $routeMiddleware
底下的名稱衝突了。
另外,如果 route 上面綁定多個 middlewares 進行檢查,若想要控制檢查的順序 (有可能某些檢查的運算量比較大),可以在 app\Http\Kernel.php
中的 $middlewarePriority
指定順序如:
/**
* The priority-sorted list of middleware.
*
* This forces non-global middleware to always be in the given order.
*
* @var array
*/
protected $middlewarePriority = [
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\Authenticate::class,
\Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Auth\Middleware\Authorize::class,
];
Laravel middleware 的處理滿容易上手,在應用上如果有可能有特殊資料或是檔案,我們可以在進入 controller 之前就過濾,讓系統更為健康。
由於 Laravel 與 Nuxt 的應用分屬不同 server 上面,因此我們必須驗證 API 使用者的資訊,包括是不是註冊會員、有沒有使用該 API 的權限等等,明天會介紹 JWT 讓 API 的使用更加安全!