iT邦幫忙

2026 iThome 鐵人賽

DAY 13
1
Security

槍林彈雨下的資安防守:從品質觀念切入,帶開發者從零動手作資安 30 天系列 第 13

【Day 13】SEI 10 大安全程式碼法則 (上):輸入驗證 (Validate Input) 與預設拒絕

  • 分享至 

  • xImage
  •  

💡 今日學習目標:深入學習 SEI (Software Engineering Institute) 前五大安全程式碼實務法則,建立防禦性程式設計底層思維。


📌 前言:世界級的程式碼防禦指南

卡內基梅隆大學 SEI 歸納的 Top 10 Secure Coding Practices,是全球軟體界公認的最權威程式碼安全指南之一。

不論你使用的是 C#、Java、Python、JavaScript 還是 Go,這 10 大法則都是跨語言萬國通用的防守鐵律。

今天,我們就來拆解前五法則的精髓與跨語言邏輯示範!


🔍 SEI 前五大法則深度拆解

SEI CERT 安全程式碼十大實務法則,今天談的是前五條


法則 1:輸入驗證 (Validate Input)

  • 通則邏輯:將所有來自外部來源的資料(URL、表單、標頭、檔案上傳)進行強型別、長度與格式驗證。
  • 白名單優先(Whitelisting):永遠採用「允許合法的字元與格式」,而非「過濾危險的字元(Blacklisting)」。
// ❌ 黑名單檢查(容易被繞過)
If userInput.Contains("<script>"): // 攻擊者可用 <sCrIpT> 或 <img src=x onerror=...> 輕易繞過
    Return Error

// ✅ 白名單檢查(嚴格規範合法格式)
If NOT Regex.IsMatch(userInput, "^[a-zA-Z0-9_]{3,20}$"):
    Return Error("Invalid Username Format")

⚠️ 用正規表達式做白名單,有兩個坑一定要避開

1. 錨點要用對,而 Ruby 要特別小心。

Python、Java、.NET、PHP 裡,$ 匹配的是「字串結尾」或「結尾換行符之前」。也就是說 admin\n<script>… 這種輸入,有機會通過 ^[a-zA-Z0-9_]+$ 的檢查。要真的鎖住整串,Python 用 \Z,Java / .NET / PHP 用 \z

Ruby 的狀況更嚴重一層:它的 ^$ 永遠是「行錨點」,沒有例外,也不需要任何旗標(Ruby 的 /m 只影響 . 能不能吃換行)。所以 /^[a-zA-Z0-9_]+$/ 的真正意思是:只要輸入裡「有任何一行」長得像使用者名稱,就通過

這代表只換結尾錨點是不夠的/^[a-zA-Z0-9_]+\z/ 拿去比對 "evil\nadmin" 依然會過,因為 ^ 跑去匹配第二行的開頭了。Ruby 必須頭尾一起換掉,用 \A\z

/\A[a-zA-Z0-9_]{3,20}\z/

(JavaScript 與 Go 沒有這個問題:$ 就是字串結尾,除非你自己開了多行模式。)

2. 小心 ReDoS。 巢狀量詞(例如 (a+)+)遇到精心構造的輸入時,會讓正規引擎陷入指數級回溯,一支請求就能把 CPU 吃滿,這正是 Day 04 冰山圖裡水面下的 ReDoS。
白名單規則寫得越簡單越好,這剛好也呼應了等一下要講的法則 4。


法則 2:重視編譯器與工具警告 (Heed Compiler Warnings)

  • 通則邏輯:把編譯器警告開到最高級別,並且把警告當成錯誤處理(C/C++ 用 -Wall -Wextra -Werror、TypeScript 開 strict、Python 用 mypy --strict、C# 開 TreatWarningsAsErrors)。
  • 資安含義:類型轉換不匹配、未使用的變數、可能的 Null 引用,往往是攻擊者構造 Memory Corruption 或 Logic Flaw 的破口。

法則 3:為安全政策設計架構 (Architect for Security Policies)

  • 通則邏輯:系統架構必須能輕鬆實施資安政策(如身分驗證、存取控制、審計日誌)。
  • 實踐:將身分驗證與授權機制抽離為統一的 Middleware 或 Filter,避免在每個業務 API 中分散重複撰寫權限檢查。

法則 4:保持簡潔與單一職責 (Keep it Simple - KISS)

  • 通則邏輯:資安專家 Bruce Schneier 有一句流傳極廣的話:「複雜是安全最大的敵人(Complexity is the worst enemy of security)」
  • 資安含義:邏輯越複雜、交織越混亂的程式碼,審查與測試就越困難,潛藏漏洞的機率呈指數上升。
  • 實務判準:如果一段權限判斷的邏輯,你沒辦法在三十秒內對同事講清楚它在做什麼,那它大概率已經藏了一個你自己也沒發現的分支。

法則 5:預設拒絕 (Default Deny)

  • 通則邏輯:在決策存取權限時,預設狀態永遠是「拒絕(Deny)」,除非有明確條件給予授權(Allow)。
// ❌ 不安全的邏輯:預設允許,特定條件才拒絕
Function CanAccessResource(user, resource):
    If user.IsBanned:
        Return False
    Return True // 預設放行!容易漏掉未考慮到的邊界情況

// ✅ 安全防守邏輯:預設拒絕,只有明確授權才允許
Function CanAccessResource(user, resource):
    If user.HasPermission(resource.RequiredPermission):
        Return True // 明確授權
    Return False // 預設全部拒絕

🎯 今日重點小結與防守心法

  • 🔹 心法 1:白名單勝於黑名單。輸入驗證必須限定允許格式,不要妄想窮舉危險字元。
  • 🔹 心法 2:預設拒絕(Default Deny)。權限系統必須採用零信任預設拒絕機制。
  • 🔹 心法 3:重視提示與警告。把 Warnings 當作潛在的 Vulnerabilities 處理。

💬 明日預告:【Day 14】SEI 10 大安全程式碼法則 (下):最小權限 (Least Privilege) 與縱深防禦
明天我們將繼續解析 SEI 後五大黃金法則,從最小權限一路談到縱深防禦!


上一篇
【Day 12】既然事後要修,為什麼不一開始就寫好?SEI Secure Coding 核心哲學
下一篇
【Day 14】SEI 10 大安全程式碼法則 (下):最小權限 (Least Privilege) 與縱深防禦
系列文
槍林彈雨下的資安防守:從品質觀念切入,帶開發者從零動手作資安 30 天14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
AndyAWD
iT邦新手 1 級 ‧ 2026-09-01 23:17:24

把編譯器警告開到最高級別,並且把警告當成錯誤處理。這句我喜歡

0
AndyAWD
iT邦新手 1 級 ‧ 2026-09-01 23:17:25

把編譯器警告開到最高級別,並且把警告當成錯誤處理。這句我喜歡

我要留言

立即登入留言