iT邦幫忙

2026 iThome 鐵人賽

DAY 12
0
Software Development

文科生的軟體工程啟蒙:用一個代購 App,看懂 30 個系統設計觀念系列 第 12 篇

Day 12:資料驗證與防禦性設計——前端資料過濾、型別邊界(Type Boundaries)與防呆

  • 分享至 

  • xImage
  •  

昨天我們確保了「操作發生的順序」不會出錯——檢查跟扣除庫存綁成同一個原子操作,不怕被插隊。但操作順序再正確,如果送進來的資料本身就是錯的、甚至是惡意的,系統一樣會壞。今天要談的資料驗證,處理的正是這一層:跨越客戶端、傳輸層和伺服器層的多層安全合約。在零信任原則下運作的現代網頁系統裡,每個邊界都需要明確的驗證,把驗證當成一種結構性的設計,而不是想到才加的開發習慣。

輸入驗證確保輸入系統(如網頁表單或應用程式)的資料符合預定義的標準,例如格式、型別和範圍。它是抵禦惡意輸入和錯誤資料的第一道防線,這些輸入和資料可能會導致 SQL 隱碼攻擊或跨網站指令碼(XSS)等漏洞。

一、前端資料過濾

前端驗證專注於使用者體驗和互動層控制。它提供即時回饋和結構化格式強制執行(例如驗證必填欄位、字串長度及正規表示式),以便在傳輸前攔截格式錯誤的資料並減少不必要的伺服器負載。然而,由於客戶端控制可以透過瀏覽器開發人員工具或直接呼叫 API 來繞過,前端驗證僅作為指導方針,不能當成安全性邊界。

二、型別邊界(Type Boundaries)

型別檢查確保每個欄位中的實際值能被解析為預期的資料型別(例如字串、整數、布林值或列舉)。在前端和後端之間實作共用的綱要定義(使用 Zod 或 TypeScript 等工具),可以防止驗證規則漂移,並為資料結構建立可靠的合約:

// 用 Zod 定義訂單的型別邊界,前後端共用同一份規則
const OrderSchema = z.object({
  price: z.number().positive(),      // 必須是正數
  quantity: z.number().int().positive(), // 必須是正整數
  currency: z.enum(['USD', 'JPY', 'KRW'])  // 只能是這三種幣別
});

// 後端收到請求後,第一件事就是驗證
const result = OrderSchema.safeParse(req.body);
if (!result.success) {
  return res.status(400).json({ error: '資料格式不正確' });
}

三、防呆設計(Defensive Design)

防禦性程式設計意味著預期每個使用者輸入都可能產生最壞的結果,並在信任邊界將其視為不受信任的資料。透過強制執行範圍檢查、一致性檢查和跨欄位邏輯,系統可以防止連鎖失敗並維持下游的資料完整性。

四、代購 App 案例

以代購 App 為例,當代購商在介面上新增商品或記錄訂單時,若僅依賴前端檢查金額與數量,惡意使用者或異常請求可能會繞過 UI,直接呼叫 API 傳送負數金額、文字型別的數量,或非預期的幣別格式,導致後端計算錯誤或資料庫損毀。強固的縱深防禦架構會透過前端即時提示輸入格式,並在後端與資料庫建立嚴格的型別邊界與條件約束(例如確保價格欄位大於 0、數量為整數,以及關聯鍵的完整性),確保每一筆訂單與代購交易在所有層級中的資料一致性與安全性。

五、結論

有效的資料驗證需要策略性的關注點分離:前端保持資訊豐富以最佳化使用者體驗,後端和資料庫則作為不妥協的受信任邊界,強制執行安全性和完整性。昨天處理的是「順序不能亂」,今天處理的是「內容不能假」——兩者合起來,才是一個系統面對真實世界時該有的基本防護。把「零信任」這個原則具體化之後,才明白為什麼前端驗證做得再完善,後端還是得重新驗證一次——不是不信任前端工程師,而是介面之外還有太多繞過畫面直接打 API 的可能性,這道防線不能省。


上一篇
Day 11:非同步處理與競態條件(Race Conditions)——Promise、Async/Await 與資料等待機制
下一篇
Day 13:什麼是壞味道(Code Smell)?——高耦合與義大利麵程式碼的形成原因
系列文
文科生的軟體工程啟蒙:用一個代購 App,看懂 30 個系統設計觀念 共 13 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言