# Lesson 11: 應用層的 Zero Trust-資料驗證 (Validation) 與 DTO

在資安領域有一個非常有名的概念叫做 Zero Trust (零信任)。它的核心精神只有一句話："Never Trust, Always Verify" (永不信任，始終驗證)。

在網路世界裡，後端工程師的第一條守則是：**「永遠不要相信客戶端傳來的輸入」。**

過去工程師常認為：「只要使用者登入了（通過驗證），他傳來的資料就是安全的」或者「這是我們自己寫的前端 App 傳來的資料，所以可以信任」。這就是傳統的**邊界防禦 (Perimeter Security)** 思維——就像城堡，進了城門就沒人管你了。

但對於後端工程師來說，**API 接口就是戰場的最前線，沒有所謂的「城牆內」**。

無論前端做了多完美的檢查，惡意攻擊者依然可以用 curl 或 Postman 直接對你的 API 灌入垃圾數據或攻擊代碼。這堂課我們探討如何建立一道堅固的防火牆，確保進入我們系統核心（業務邏輯與資料庫）的數據是乾淨、合法且型別正確的

# 為什麼要用 Zero Trust 看待輸入資料？

將 Zero Trust 的哲學應用在資料驗證上，我們可以這樣理解：

* **傳統思維**：
    
    * 「前端有寫 JavaScript 檢查 Email 格式了，後端不用再檢查一次吧？」
        
    * 「這是內部管理後台，只有員工會用，不用防 SQL Injection 吧？」
        
    * **後果**：一旦前端驗證被繞過（攻擊者直接用 Postman 打 API），後端完全不設防，直接讓惡意資料長驅直入。
        
* 零信任思維 (Zero Trust Security)：
    
    * 假設前端已經淪陷：我們假設發送請求的不是你的前端 App，而是一個正在嘗試注入惡意語法的駭客。
        
    * 身份不代表清白：即使請求帶有合法的 JWT (Token)，代表他是「合法的使用者」，但不代表他帶來的資料是「無害的」。
        
    * 微觀邊界 ：每一個 API Endpoint 都是一個獨立的檢查站。不管資料從哪裡來，進入這個函式之前，必須先經過嚴格的驗證。
        

# DTO (Data Transfer Object)

在我的開發生涯中，曾經看過有工程師 直接把 HTTP Request 的 JSON Body 整個丟進資料庫模型（Model）裡。這是極度危險的行為(大量指派漏洞）。

**DTO 模式** 的核心思想是：建立一個專門的物件，用來定義「這個 API 接口預期接收什麼樣的資料」。

* **沒有 DTO (危險)**：
    
    * 客戶端傳送 `{ "username": "admin", "role": "superuser" }`。
        
    * 後端直接接收並寫入資料庫 -&gt; 一般使用者突然變成了超級管理員。
        
* **使用 DTO (安全)**：
    
    * 後端定義一個 `UpdateProfileDTO`，裡面只有 `{ "username": string }`。
        
    * 即使客戶端多傳了 `role`，因為 DTO 裡沒定義，系統會自動忽略或報錯。
        

# 驗證的三個層次

一個成熟的後端架構，驗證通常發生在不同階段：

## Layer 1: 結構與型別驗證 (Syntactic Validation)

這是最外層的過濾，通常由 **Schema Validator** 處理。如果不通過，程式根本不應該進入業務邏輯。

* **檢查項目**：
    
    * 欄位是否存在？(Required)
        
    * 資料型別對不對？(Is it a String, Integer, UUID?)
        
    * 格式對不對？(Email format, Date format ISO8601)
        
* **通用工具**：
    
    * **Python**: Pydantic
        
    * **Node.js/TS**: Zod, Joi, class-validator
        
    * **Go**: Struct Tags (`binding:"required,email"`)
        
    * **JSON Schema**: 跨語言的標準定義
        

## Layer 2: 語意與邏輯驗證 (Semantic Validation)

這層涉及資料的「內容」是否合理。

* **檢查項目**：
    
    * 數值範圍：年齡不能是負數，庫存不能小於 0。
        
    * 依賴關係：如果 `payment_method` 是信用卡，則 `card_number` 必填。
        
    * 商業規則：開始時間不能晚於結束時間。
        

## Layer 3: 資料庫完整性驗證 (Database Integrity)

這是最後一道防線，通常涉及對資料庫的查詢。

* **檢查項目**：
    
    * 唯一性檢查 (Unique)：這個 Email 是否已經被註冊過？
        
    * 關聯性檢查 (Foreign Key)：這個 `product_id` 是否真的存在於商品表中？
        

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">在Laravel中，通常會封裝Form Request來解決Layer1 + Layer2</div>
</div>

# Fail Fast 原則

後端驗證的核心哲學是 **Fail Fast (快速失敗)**。 一旦發現資料不合法，立刻回傳 `400 Bad Request` 或 `422 Unprocessable Entity`，不要讓錯誤的資料繼續往後跑，浪費運算資源或導致更深層的錯誤（例如資料庫報錯）。

# 補充

## 關於DTO

DTO , Data transfer(to) Object。本質是「資料傳輸的載體」，它不只用來「收（Input）」資料，也非常適合用來「發（Output）」資料。

我們上面談了 DTO 如何像盾牌一樣防禦惡意輸入 (Input DTO)，但 DTO 還有另一個強大的功能：規範輸出的形狀 (Output DTO / Response DTO)。

如果我們直接把資料庫撈出來的 Model 直接 `return` 給前端或第三方。這會導致兩個問題：

1. 洩漏敏感資料：不小心把 `password_hash`、`soft_delete_flag` 或內部 `id` 傳出去。
    
2. 格式不符需求：資料庫存的是 `created_at` (Timestamp)，但對方要的是 XML 格式的 `<publishedDate>`。
    

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Laravel 在Model的地方可以設定 $hidden 來解決問題1。可以用transform(on Model Collection)來解決問題2。</div>
</div>

### 實戰場景：整個電商網站商品 XML Feed

假設你需要生成一個 XML 檔案給 Google Merchant Center 或比價網爬蟲使用。你的資料庫結構可能很複雜，但對方要求的格式很死板。這時，你可以用 DTO 來做「結構化映射」。

**情境**：

* 資料庫 (DB)：`products` 表，欄位有 `id`, `name`, `cost_price`, `sale_price`, `stock_qty`, `supplier_id`。
    
* 需求 (XML)：需要 `<item><title>...</title><price>...</price></item>`。
    

使用 Output DTO 的思維：

```php
// 這是我們定義的「模具」，不管資料庫怎麼變，輸出的格式永遠長這樣
class ProductXmlDTO
{
    public string $title;
    public float $price;
    public string $availability;

    // 透過建構子或工廠方法，把 DB Model 轉換成 DTO
    public static function fromModel(Product $product): self
    {
        $dto = new self();
        // 1. 改名 (Mapping)：DB 的 name 對應 XML 的 title
        $dto->title = $product->name;
        
        // 2. 邏輯轉換 (Transformation)：DB 存的是成本與售價，這裡只輸出最終售價
        $dto->price = $product->sale_price;
        
        // 3. 狀態判斷 (Logic)：將數字庫存轉為文字描述
        $dto->availability = $product->stock_qty > 0 ? 'in stock' : 'out of stock';
        
        return $dto;
    }
}
```

### 這樣做的好處

1. 解耦 (Decoupling)：如果有一天你的資料庫欄位 `name` 改成了 `product_name`，你只需要修改 DTO 的 `fromModel` 方法，外面的 XML 輸出完全不會壞掉。
    
2. 單一資料來源 (SSOT)：你可以針對不同的需求製作不同的 DTO（例如 `ProductCardDTO` 給手機版列表用，`ProductDetailDTO` 給詳情頁用），而不是讓前端自己去撈一堆不需要的欄位。
    

## 大量指派漏洞(Mass Assignment Vulnerability)

這裡我們來談談什麼是大量指派漏洞，用一個具體情境來說明：

1. 你有一個「更新使用者資料」的功能，讓使用者編輯自己的個人資訊（profile）。
    
2. 你提供一個 `PATCH/PUT` API 供前端送出更新請求。
    
3. 在 users table 中，你有一個 `remember_token` 欄位，用於實現「持久登入」。
    
4. 客戶端在呼叫更新 API 時，body 中不小心也包含了 `remember_token`。
    
5. 後端程式如果直接使用 `$request->all()` 去更新資料模型（`User::update($request->all())`），那麼 `remember_token` 就會在你完全未察覺的情況下被改寫。
    

這就是典型的 大量指派漏洞：

攻擊者或不小心的客戶端，只要傳入一個你沒預期要更新的欄位，就能成功覆寫你的資料。

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Laravel 的 $fillable 與 $guarded ，但只能做欄位白名單/黑名單的基本防護，無法取代真正的資料驗證流程。</div>
</div>
