iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 12
1
Modern Web

RRR撞到不負責之 Laravel + Nuxt.js 踩坑全紀錄系列 第 12

Day 12. HiNet 有小天使,Laravel 有 Middleware !

很多時候我們對 request 內容要有比較複雜的檢查與過濾機制,例如說呼叫 API 的授權檢查,或是針對帶入資料是否含有 XSS 攻擊的可能性等諸如此類比較複雜性的檢查,這時候可以利用 Laravel 提供的 middleware 機制有效的達成需求。今天介紹的包含下列項目,我們會以 XSS 過濾為例,逐一說明。

Middleware 基本用法

  1. 按照慣例,在 cmd 中執行下列指令會在 app\Http\Middleware 底下產生 middleware。
php artisan make:middleware <middleware 名稱>
  1. 預設的 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);
    }
}
  1. XSS 的過濾情境為: 「所有 request body 和 query string 帶進來的資料都需要檢查是否有 HTML 特殊字元,且可以根據不同 api 的需求,做出包括『拋出錯誤』、『HTML encode』 和 『不處理』三種不同的處理」。實作如下:
    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

完成 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 測試結果如下:
    https://ithelp.ithome.com.tw/upload/images/20190913/20112580UhAUz4pss3.png
    可以看到,因為 request 的 userId 包含 HTML 特殊字元,因此不會顯示「通過」訊息,而是顯示 「XSS警告...」 訊息。

  • xss:encode 測試結果如下:
    https://ithelp.ithome.com.tw/upload/images/20190913/20112580HVFVSJbgib.png
    $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 的使用更加安全!


上一篇
Day 11. 第一個 Laravel API 終於生出來惹 (´;ω;`)
下一篇
Day 13. Knock knock! Who’s there? JSON Web Token (JWT)
系列文
RRR撞到不負責之 Laravel + Nuxt.js 踩坑全紀錄31

尚未有邦友留言

立即登入留言