文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载過濾使用者資料是 Web 應用安全的基石——多數漏洞CSRF、XSS、SQL 注入的根源都是對外部輸入的「誤信」與「誤用」。本篇文章以《build-web-application-with-golang》9.2 小節為核心系統講解輸入過濾的「識別資料 → 過濾資料 → 區分已過濾與被汙染資料」三步驟並結合格語言標準庫的strconv、string、regexp套件以及倉庫內的實際驗證器程式碼讓你能夠寫出可複製、可執行的白名單過濾邏輯從源頭堵住惡意資料的注入。為什麼要把輸入過濾當成安全的第一道防線過濾使用者資料是 Web 應用安全的基礎。它是驗證資料合法性的過程。透過對所有的輸入資料進行過濾可以避免惡意資料在程式中被誤信或誤用。大多數 Web 應用的漏洞都是因為沒有對使用者輸入的資料進行恰當過濾所引起的。這段話點出了 Web 安全最重要的心法在驗證之前任何外部資料都必須被視為不安全資料。如果直接把不安全的資料輸出到客戶端就可能造成跨站指令碼攻擊XSS見 9.3 小節如果把它用於資料庫查詢就可能造成 SQL 注入。而本章前一節介紹的 CSRF 攻擊 同樣源於對請求資料缺乏嚴格校驗。所謂「輸入」並不僅限於鍵盤敲入的內容。凡是源自非程式碼內部提供的資料都屬於需要過濾的範疇所有來自客戶端的資料、資料庫中的記錄、第三方提供的介面資料等。因此一個完整的過濾方案應該分為三個步驟識別資料搞清楚需要過濾的資料來自哪裡過濾資料弄明白我們需要什麼樣的資料區分已過濾及被汙染資料如果存在攻擊資料保證過濾之後可以使用更安全的資料。下面逐一展開。識別資料先搞清楚「資料是什麼、來自哪裡」「識別資料」之所以是第一步是因為在不知道「資料是什麼、它來自於哪裡」的前提下你就不可能正確地過濾它。這裡的資料指所有源自非程式碼內部提供的資料例如所有來自客戶端的資料最常見的輸入來源資料庫中由外部寫入的記錄第三方提供的介面資料等。Go 中最容易識別的輸入r.Form由使用者輸入的資料在 Go 中非常容易識別透過r.ParseForm()之後Go 會把使用者 POST 和 GET 的資料全部放在r.Form裡面。之後無論是用r.Form.Get(name)取得單個欄位還是遍歷r.Form檢查所有欄位都能拿到原始輸入。倉庫中的範例 ch.4.4/main.go 展示了標準用法func checkProfile(w http.ResponseWriter, r *http.Request) { var errs validator.Errors r.ParseForm() token : r.Form.Get(token) ... p : validator.ProfilePage{r.Form} errs p.GetErrors() ... }注意r.Form的型別是url.Values即map[string][]string同一個欄位可能對應多個值這在設計驗證器時要格外留意倉庫的驗證器就專門區分了單值驗證與多值驗證詳見下文。容易遺漏的輸入r.Header等隱性資料源其它的輸入要難識別得多。例如r.Header中的很多元素是由客戶端所操縱的——User-Agent、Referer、Accept-Charset等請求頭都可以被攻擊者偽造。常常很難確認其中的哪些元素組成了輸入所以最好的方法是把裡面所有的資料都看成是使用者輸入。即便是r.Header.Get(Accept-Charset)這樣看似由瀏覽器操縱的值也應視為使用者輸入來對待因為攻擊者完全可以透過自製 HTTP 請求任意改寫請求頭。識別資料的核心結論凡是你能夠被外部影響的值都列入過濾清單寧可多過濾不可漏過濾。過濾資料驗證、清潔與淨化的本質在知道資料來源之後就可以過濾它了。「過濾」是一個有點正式的術語它在平時表述中有很多同義詞如驗證、清潔及淨化。儘管這些術語表面意義不同但它們都是指同一個處理防止非法資料進入你的應用。過濾的正確心態檢查而非「好心糾正」過濾資料有很多種方法其中有一些安全性較差。最好的方法是把過濾看成一個檢查的過程——在你使用資料之前都檢查一下看它們是否符合合法資料的要求。而且不要試圖好心地去糾正非法資料而要讓使用者按你制定的規則去輸入資料。歷史證明了試圖糾正非法資料往往會導致安全漏洞。例如「最近建設銀行系統升級之後如果密碼後面兩位是 0只要輸入前面四位就能登入系統」——這是一個非常嚴重的漏洞根源正是系統為了「友好」地幫使用者補全/糾正輸入反而放寬了校驗標準。類比到程式設計上使用者提交了username astaxie 帶空格與其靜默幫他 Trim 後放行不如明確告知他必須按規則輸入如果一定要做寬鬆處理也必須在明確定義規則的前提下進行。三大過濾函式庫在 Go 中過濾資料主要採用如下一些函式庫來操作套件典型函式用途strconvAtoi、ParseBool、ParseFloat、ParseInt從r.Form回傳的字串轉化成整數/浮點數並在轉化失敗時判定非法stringTrim、ToLower、ToTitle按照指定格式取得資訊規範化字串內容regexpMatchString等處理複雜需求例如判定輸入是否是 Email、生日等格式strconv型別轉換即驗證因為從 Request 中的r.Form回傳的是字串而有些時候我們需要將之轉化成整/浮點數。Atoi、ParseBool、ParseFloat、ParseInt等函式就可以派上用場了。關鍵技巧是型別轉換失敗本身就是一個驗證失敗的信號。倉庫中的驗證器 checkAge 就是這個模式的典範——先用strconv.Atoi嘗試轉換轉換失敗即報錯並進一步校驗數值範圍13130 歲// Check if age is a number and between 13 and 130 func checkAge(str string) error { age, err : strconv.Atoi(str) if str || err ! nil { return errors.New(Please enter a valid age.) } if age 13 { return errors.New(You must be at least 13 years of age to submit.) } if age 130 { return errors.New(Youre too old to register, grandpa.) } return nil }string字串規範化string套件下的Trim、ToLower、ToTitle等函式能夠幫助我們按照指定的格式取得資訊。例如判斷使用者名稱是否為空時先strings.Trim(str, )去除首尾空格再判斷可以避免「純空格」偽裝成有效輸入——倉庫的 checkUsername 正是這樣實作的func checkUsername(str string) error { if strings.Trim(str, ) { return errors.New(Please enter a username.) } return nil }regexp複雜格式的守門員regexp套件用來處理一些複雜的需求例如判定輸入是否是 Email、生日之類別。倉庫中的 checkEmail 用一個簡單正則攔截不含的偽裝地址func checkEmail(str string) error { if m, err : regexp.MatchString(^[^][^]$, str); !m { fmt.Println(err , err) return errors.New(Please enter a valid email address.) } return nil }checkChineseName 則用 Unicode 範圍正則^[\x{4e00}-\x{9fa5}]$確保中文名只含中文字元——這正對應了 9.2 小節「判定輸入是否符合特定格式」的應用場景。白名單預設非法證明清單內才合法過濾資料除了檢查驗證之外在特殊時候還可以採用白名單。即假定你正在檢查的資料都是非法的除非能證明它是合法的。使用這個方法如果出現錯誤只會導致把合法的資料當成是非法的而不會是相反——儘管我們不想犯任何錯誤但這樣總比把非法資料當成合法資料要安全得多。白名單思維與「糾正非法資料」形成鮮明對比糾正法預設「輸入是對的我幫你修」白名單法預設「輸入是錯的你證明給我看」。後者在安全上永遠是更保守、更穩妥的選擇。區分過濾資料CleanMap 防汙染注入如果完成了上面的兩步資料過濾的工作就基本完成了但是在編寫 Web 應用的時候我們還需要區分已過濾和被汙染資料因為這樣可以保證過濾資料的完整性而不影響輸入的資料。我們約定把所有經過過濾的資料放入一個叫全域的 Map 變數中CleanMap。這時需要用兩個重要的步驟來防止被汙染資料的注入每個請求都要初始化CleanMap為一個空 Map——避免上一次請求的殘留資料混入本次請求造成跨請求的資料污染加入檢查及阻止來自外部資料來源的變數命名為CleanMap——也就是說外部輸入如r.Form中的欄位永遠不能直接作為CleanMap的鍵或值進入程式所有寫入CleanMap的資料必須先經過驗證。簡言之CleanMap是「已信任資料」的唯一下水道只有通過白名單/驗證的資料才允許流入其餘一概隔離在應用邏輯之外。這樣程式碼的其它部分只需讀取CleanMap就永遠不會碰到未經驗證的汙染資料。實戰一表單欄位的白名單過濾接下來讓我們透過一個例子來鞏固這些概念。請看下面這個表單——一個「我是誰」的下拉選擇框form action/whoami methodPOST 我是誰: select namename option valueastaxieastaxie/option option valueherryherry/option option valuemarrymarry/option /select input typesubmit / /form在處理這個表單的程式設計邏輯中非常容易犯的錯誤是認為只能提交三個選擇中的一個。其實攻擊者可以模擬 POST 操作提交nameattack這樣的資料——瀏覽器介面的下拉框限制對攻擊者來說形同虛設因為他根本不經過瀏覽器 UI而是直接構造 HTTP 請求。所以在此時我們需要做類似白名單的處理r.ParseForm() name : r.Form.Get(name) CleanMap : make(map[string]interface{}, 0) if name astaxie || name herry || name marry { CleanMap[name] name }上面程式碼中我們初始化了一個CleanMap的變數當判斷取得的name是astaxie、herry、marry三個中的一個之後我們把資料儲存到了CleanMap之中這樣就可以確保CleanMap[name]中的資料是合法的從而在程式碼的其它部分使用它。當然我們還可以在else部分增加非法資料的處理一種可能是再次顯示錶單並提示錯誤。但是不要試圖為了友好而輸出被汙染的資料——直接把未經驗證的輸入回顯到頁面本身就是 XSS 的常見入口見 9.3 小節 的反射型 XSS 原理。與倉庫驗證器的對照白名單的工程化寫法這個「枚舉合法值」的思路在倉庫 ch.4.2/validator/main.go 中被工程化為「驗證器表」用一個map[string]func(string) error把每個表單欄位名對應到它的驗證函式GetErrors遍歷整個表單逐欄位驗證。例如 checkGender 和 checkShirtSize 本質上就是「合法值集合」的白名單func checkGender(str string) error { if str { return nil } siblings : []string{m, f, na} if !isElementInSlice(str, siblings) { return errors.New(Please select a valid gender.) } return nil }這種「白名單集合 查表驗證」的模式比逐欄位手寫if...else更易維護、更難遺漏——新增一個欄位只需要註冊對應的驗證函式即可。實戰二字元集白名單正則約束上面的方法對於過濾一組已知的合法值的資料很有效但是對於過濾有一組已知合法字元組成的資料時就沒有什麼幫助。例如你可能需要一個使用者名稱只能由字母及數字組成r.ParseForm() username : r.Form.Get(username) CleanMap : make(map[string]interface{}, 0) if ok, _ : regexp.MatchString(^[a-zA-Z0-9]$, username); ok { CleanMap[username] username }這就體現了兩類白名單的區別值白名單合法資料是一組可窮舉的離散值如性別m/f/na、尺碼s/m/l/xl/xxl適合用集合比對模式白名單合法資料滿足某種字元模式如「僅字母與數字」的^[a-zA-Z0-9]$適合用正則表達式。正則白名單同樣遵循「預設非法」原則^...$的錨點保證整個字串都必須匹配而不是「包含」合法字元即可——否則attackscript這種字串也能通過[a-zA-Z0-9]的包含匹配混入系統。倉庫中 checkChineseName 的^[\x{4e00}-\x{9fa5}]$正是同一思路的應用。總結把過濾當成 Web 應用的基石資料過濾在 Web 安全中起到一個基石的作用。大多數的安全問題都是由於沒有過濾資料和驗證資料引起的例如前面小節的 CSRF 攻擊以及接下來將要介紹的 XSS 攻擊、SQL 注入9.4 小節等都是沒有認真地過濾資料引起的因此我們需要特別重視這部分的內容。回顧本小節的三個核心行動準則識別資料r.ParseForm()之後r.Form是最主要的輸入源r.Header等一切可被客戶端影響的值也要視為輸入過濾資料善用strconv型別轉換即驗證、stringTrim/ToLower 規範化、regexp格式白名單並堅持「檢查而非糾正」區分資料每個請求初始化空的CleanMap只有驗證通過的資料才能寫入外部輸入永遠不能直接污染已信任資料區。在動手寫業務邏輯之前先把「輸入從哪裡來、允許長什麼樣、如何進入可信區」這三件事設計清楚你的 Go Web 應用就已經擋住了絕大多數因「誤信輸入」而產生的安全漏洞。延伸閱讀本章總覽9 安全與加密上一節預防 CSRF 攻擊下一節避免 XSS 攻擊書籍目錄preface.md可執行的驗證器範例ch.4.2/validator/main.go結合 token 防重複提交與輸入驗證的完整範例ch.4.4/main.go赞分享文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载相关推荐beego 表單及驗證支援用 struct 宣告式構建表單並完成後端資料校驗beego 表單及驗證支援用 struct 宣告式構建表單並完成後端資料校驗 本指南聚焦於《Go Web 程式設計》第十四章「擴充套件 Web 框架」中介紹的文档教程beego 框架六大擴充實戰靜態文件、Session、表單驗證、用戶認證、i18n 與 pprof 全解析beego 框架六大擴充實戰靜態文件、Session、表單驗證、用戶認證、i18n 與 pprof 全解析 第十四章是《Go Web 編程》中「擴充套件 We文档教程ChatLab 聊天資料交換標準化格式ChatLab Format v0.0.2完整指南JSON / JSONL 結構、訊息類型與驗證實務ChatLab 聊天資料交換標準化格式ChatLab Format v0.0.2完整指南JSON / JSONL 結構、訊息類型與驗證實務 ChatLab人工智能AI Agent数据分析桌面应用后端前端即时通讯MCP 服务本地部署上一篇Angular模块联邦终极指南构建可扩展微前端架构的完整教程下一篇终极指南Claude Code的Playwright浏览器自动化技能深度解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考