Skip to main content

Command Palette

Search for a command to run...

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

Updated
3 min read
B

一個很忙的工程師,比起寫code更喜歡股票,QQQ買起來,謝謝!

在資安領域有一個非常有名的概念叫做 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" }

    • 後端直接接收並寫入資料庫 -> 一般使用者突然變成了超級管理員。

  • 使用 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 是否真的存在於商品表中?

💡
在Laravel中,通常會封裝Form Request來解決Layer1 + Layer2

Fail Fast 原則

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

補充

關於DTO

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

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

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

  1. 洩漏敏感資料:不小心把 password_hashsoft_delete_flag 或內部 id 傳出去。

  2. 格式不符需求:資料庫存的是 created_at (Timestamp),但對方要的是 XML 格式的 <publishedDate>

💡
Laravel 在Model的地方可以設定 $hidden 來解決問題1。可以用transform(on Model Collection)來解決問題2。

實戰場景:整個電商網站商品 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 的思維:

// 這是我們定義的「模具」,不管資料庫怎麼變,輸出的格式永遠長這樣
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 就會在你完全未察覺的情況下被改寫。

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

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

💡
Laravel 的 $fillable 與 $guarded ,但只能做欄位白名單/黑名單的基本防護,無法取代真正的資料驗證流程。

從Rookie到Junior,一個後端成長的30堂課

Part 18 of 31

“從 Rookie 到 Junior:一個後端成長的 30 堂課” 是一套專為後端新手所設計的成長型技術系列文章。內容以實務為導向,逐步拆解後端工程的核心能力,包括程式語言基礎、架構思維、框架運作原理、業務邏輯設計、資料庫操作、以及常見的開發模式。 本系列的目標是協助讀者從零散的學習堆疊,建立成體系的後端知識框架。讀者能夠理解各語言背後不變的工程思維與設計原則。這套內容旨在讓學習者從「會寫程式」進入「能理解系統設計」的階段,逐步具備勝任 Junior Backend Engineer 的能力。

Up next

Lesson 10: 守門員的權限:授權 - RBAC 與 Policy 設計

在上一堂課,我們學會了如何驗證「你是誰」(Authentication)。但確認身分只是第一步。 想像你是一間銀行的員工。你刷卡通過大門(Authentication 成功),但这不代表你可以直接走進金庫搬錢。「你能做什麼?」 這就是 授權 (Authorization) 的範疇。 對於後端工程師來說,如果說 Authentication 是守大門的警衛,那麼 Authorization 就是每一扇門上的電子鎖與守門員,它決定了請求是否會被拒絕(403 Forbidden)。 核心觀念:Auth...

More from this blog

Lesson 26 : 系統韌性的守護者-限流、熔斷與背壓的設計模式

當這幾個名詞出現後,代表我們進到了一個高併發/大流量的系統了。在這個章節中,我們一起來看看如何透過一些方式來避免高併發導致我們的系統crash掉。 限流(Rate Limiting) 相信大家對這個名詞並不陌生,限流其實就是字面上的含意,限制流量。 限流的目的是保護「接收方」,確保系統不會因為瞬間的高併發請求而癱瘓。 限流通常發生在 API Gateway 或服務的最前端。它像是一個夜店門口的保全

Mar 26, 20262 min read

Lesson 25: 淺談 單體架構、微服務架構與單/多租戶架構

過去我們討論了 要把程式寫在哪、程式要怎麼拆的題目,接下來我們來看看「如何服務不同客戶」,這些架構反映了軟體開發在擴充性與複雜度之間的權衡。 軟體架構深度解析:從系統拆分到商業規模化 在軟體工程的演進中,架構的選擇往往是在「開發效率」、「系統擴充性」與「營運成本」之間尋求平衡。我們可以從兩個核心維度來觀察這些架構:系統如何運行(單體 vs. 分散式) 以及 如何服務客戶(單租戶 vs. 多租戶)。

Mar 26, 20262 min read

Lesson 24: 資料庫擴展術-讀寫分離、複寫機制與快取一致性挑戰

為什麼要讀寫分離? 大多數的 Web 應用都是 「讀多寫少」(例如:看文的人多,發文的人少,Heavy Read System)。當所有的請求都塞給同一台資料庫時,磁碟 I/O 和連線數會成為瓶頸。 Master (主庫): 負責寫入 (Insert/Update/Delete),確保數據一致性。 Slave (從庫): 負責讀取 (Select),可以有多個從庫來分擔讀取壓力。 為什麼讀

Mar 25, 20262 min read

面試經驗談 2025-2026

從2025年3月開始,我陸陸續續參與了從新創到上市櫃公司的Senior - Tech Lead的相關面試,其中有不乏 尊重面試者、展現高度專業的企業(公司),當然也有遇到幾場面試鬼故事,這篇文章主要分享我對於軟體工程師面試的方向分享,以及部分鬼故事,以此警惕自己不要成為這樣的面試官。 AI的洪流,改變了SWE的生態 LLM的發展確確實實的影響到了軟體工程師的生態。 過去受限於算力與資料規模,深度學

Mar 23, 20262 min read

Lesson 23: 系統的緩衝區-Queue 佇列與非同步處理 (Asynchronous)

佇列 佇列的實作工具非常多,舉凡AWS SQS、RabbitMQ、Kafka…等。 佇列的特性,其實是一個非常強大的系統緩衝區,應用層面非常廣。 什麼是佇列? 佇列可以想像成,在既有流程中外,有另一個”水管”,來連接原有的資料流(或邏輯過程),其中 呼叫方將資料 推(Push)到水管中,接受方(監聽) 從水管中將資料拉(Pull)出處理 為什麼佇列是「強大的緩衝區」? 在同步處理中,系統像是一

Mar 23, 20262 min read

Bennett's Tech Blog | 後端架構、系統設計

32 posts

來自台灣的軟體工程師,相信軟體可以改變世界