客戶端 IP)
Gin 怎么配置 SetTrustedProxies 與 TrustedPlatform 讓 ClientIP 返回真實(shí)客戶端 IP【免費(fèi)下載鏈接】ginGin is a high-performance HTTP web framework written in Go. It provides a Martini-like API but with significantly better performance—up to 40 times faster—thanks to httprouter. Gin is designed for building REST APIs, web applications, and microservices.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/gi/ginGin 服務(wù)放在反向代理或 CDN 后面時(shí)c.ClientIP()應(yīng)該返回真實(shí)客戶端 IP而不是代理的地址。但 Gin 默認(rèn)信任所有代理TrustedProxies功能默認(rèn)開啟且默認(rèn)信任全部來源這意味著任何人都可以偽造X-Forwarded-For頭。這篇文檔給出兩條互補(bǔ)的配置路徑用Engine.SetTrustedProxies()限定哪些直接連接來源的 IP 頭可以被信任自建代理場(chǎng)景以及在接入 CDN 時(shí)用Engine.TrustedPlatform直接讀取 CDN 寫入的專用頭。前提是一個(gè)已引入 Gin 的 Go 項(xiàng)目。ClientIP() 的判定鏈先看 context.go 中ClientIP()的注釋和實(shí)現(xiàn)它按以下順序決定返回值TrustedPlatform非空且請(qǐng)求中存在該名稱的頭 → 直接返回該頭的值否則從Request.RemoteAddr解析直接連接 IPRemoteIP()若直接連接 IP 落在SetTrustedProxies()設(shè)置的信任列表內(nèi)且ForwardedByClientIP為truegin.go 中New()的默認(rèn)值就是true則解析Engine.RemoteIPHeaders中定義的頭默認(rèn)[X-Forwarded-For, X-Real-IP]頭不合法、或直接連接 IP 不受信任 → 返回直接連接 IP。其中第 3 步的具體規(guī)則在 gin.go 的validateHeader中實(shí)現(xiàn)X-Forwarded-For按逗號(hào)拆分后從后往前檢查返回第一個(gè)不在信任列表中的 IP如果所有 IP 都在信任列表里返回頭中的第一個(gè) IP遇到無法解析的條目則停止并判定該頭無效。起步檢查默認(rèn)配置會(huì)打印警告Gin 的默認(rèn)信任列表是0.0.0.0/0與::/0gin.go 中New()的初始值即信任全部代理。docs/doc.md 明確標(biāo)注this is NOT safe。Run、RunTLS、RunUnix、RunFd、RunQUIC、RunListener這些啟動(dòng)入口都會(huì)檢查信任列表是否覆蓋全部 IP若是則打印[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.因此啟動(dòng)時(shí)是否出現(xiàn)這條警告可以作為一條現(xiàn)成的核對(duì)信號(hào)配置正確后它不應(yīng)再出現(xiàn)。步驟一用 SetTrustedProxies 限定可信代理按 docs/doc.md 的 Dont trust all proxies 一節(jié)在gin.Engine上調(diào)用SetTrustedProxies()參數(shù)支持 IPv4 地址、IPv4 CIDR、IPv6 地址、IPv6 CIDR。文檔給出的示例192.168.1.2替換為你自己的代理地址func main() { router : gin.Default() router.SetTrustedProxies([]string{192.168.1.2}) router.GET(/, func(c *gin.Context) { // If the client is 192.168.1.2, use the X-Forwarded-For // header to deduce the original client IP from the trust- // worthy parts of that header. // Otherwise, simply return the direct client IP fmt.Printf(ClientIP: %s\n, c.ClientIP()) }) router.Run() }router.Run()未指定端口時(shí)默認(rèn)監(jiān)聽:8080除非設(shè)置了PORT環(huán)境變量文檔路由章節(jié)有說明。幾個(gè)直接影響執(zhí)行的細(xì)節(jié)返回值是error必須檢查。gin_test.go 的TestPrepareTrustedCIRDsWith展示了哪些輸入合法192.168.1.33合法單個(gè) IP 會(huì)被解析為/322002:0000:0000:1234:abcd:ffff:c0a8:0101合法IPv6 單地址解析為/128::/0、192.168.0.0/16等組合合法而192.168.1.33/33、192.168.1.256、gggg:0000:0000:1234:abcd:ffff:c0a8:0101這類無效值會(huì)返回 error。完全不使用代理時(shí)用router.SetTrustedProxies(nil)禁用該功能此時(shí)Context.ClientIP()直接返回直接連接地址避免不必要的計(jì)算——這是 docs/doc.md 給出的做法文檔原文說明其目的是 avoid some unnecessary computation。列表為空切片[]string{}與nil效果不同nil是關(guān)閉功能空切片是沒有任何可信代理。兩者的結(jié)果都是ClientIP()回退到直接連接 IP見 context_test.go 中兩條相鄰的測(cè)試斷言但文檔明確描述的是nil這一種優(yōu)先使用文檔說明的寫法。步驟二接入 CDN 時(shí)改用 TrustedPlatformdocs/doc.md 的 Notice 指出Engine.TrustedPlatform可以跳過 TrustedProxies 檢查它的優(yōu)先級(jí)高于 TrustedProxies。設(shè)置后ClientIP()會(huì)解析對(duì)應(yīng)的頭并直接返回其中的 IP。gin.go 中預(yù)定義了三個(gè)平臺(tái)常量對(duì)應(yīng)各自讀取的頭常量讀取的請(qǐng)求頭gin.PlatformGoogleAppEngineX-Appengine-Remote-Addrgin.PlatformCloudflareCF-Connecting-IPgin.PlatformFlyIOFly-Client-IP文檔示例192.168.1.2一節(jié)的同款結(jié)構(gòu)此處為 CDN 分支func main() { router : gin.Default() // Use predefined header gin.PlatformXXX // Google App Engine router.TrustedPlatform gin.PlatformGoogleAppEngine // Cloudflare router.TrustedPlatform gin.PlatformCloudflare // Fly.io router.TrustedPlatform gin.PlatformFlyIO // Or, you can set your own trusted request header. But be sure your CDN // prevents users from passing this header! For example, if your CDN puts // the client IP in X-CDN-Client-IP: router.TrustedPlatform X-CDN-Client-IP router.GET(/, func(c *gin.Context) { // If you set TrustedPlatform, ClientIP() will resolve the // corresponding header and return IP directly fmt.Printf(ClientIP: %s\n, c.ClientIP()) }) router.Run() }注意文檔的警告如果用自己的頭名如X-CDN-Client-IP必須確認(rèn)你的 CDN 會(huì)阻止用戶直接傳入該頭否則客戶端仍可偽造。另外當(dāng)請(qǐng)求中該頭為空時(shí)ClientIP()不會(huì)報(bào)錯(cuò)而是回落到 TrustedProxies 判定路徑context_test.go 中有對(duì)應(yīng)斷言TrustedPlatform指向一個(gè)不存在的頭時(shí)返回直接連接 IP。驗(yàn)證配置是否生效1. 啟動(dòng)檢查運(yùn)行服務(wù)確認(rèn)[WARNING] You trusted all proxies, this is NOT safe.不再出現(xiàn)在啟動(dòng)日志中。2. 請(qǐng)求檢查按步驟一/二的示例程序運(yùn)行用curl http://localhost:8080/發(fā)請(qǐng)求觀察 handler 打印的ClientIP:輸出是否符合預(yù)期。context_test.go 的ClientIP測(cè)試用例給出了一組可參照的行為數(shù)據(jù)以下為測(cè)試用例中的示例值不是生產(chǎn)環(huán)境的固定預(yù)期RemoteAddr為40.40.40.40:42123X-Forwarded-For為20.20.20.20, 30.30.30.30信任列表只含40.40.40.40→ClientIP()返回30.30.30.30從后往前第一個(gè)不受信任的 IP信任列表包含40.40.40.40、30.30.30.30、20.20.20.20全部受信任→ 返回頭中第一個(gè) IP20.20.20.20信任列表用 CIDR40.40.25.25/1630.30.30.30時(shí)直接連接40.40.40.40命中該 CIDR同樣返回20.20.20.20直接連接不受信任 → 返回直接連接 IP40.40.40.40X-Forwarded-For內(nèi)容不是合法 IP如 blah → 回退返回直接連接 IP。3. 錯(cuò)誤檢查SetTrustedProxies返回非 nil 的error說明列表里有無法解析的地址或 CIDR需要修正輸入格式見上一步的合法/非法示例。限制與已廢棄項(xiàng)默認(rèn)信任所有代理是不安全狀態(tài)docs/doc.md 原文標(biāo)注this is NOT safe不配置SetTrustedProxies或接入 CDN 前的狀態(tài)都應(yīng)視為待修正項(xiàng)。Engine.AppEngine字段已廢棄gin.go 中的注釋要求改用TrustedPlatform并取值gin.PlatformGoogleAppEngine。舊標(biāo)志仍然生效讀取X-Appengine-Remote-Addr頭但每次請(qǐng)求都會(huì)打印一條 deprecation 日志context.go 中ClientIP()的實(shí)現(xiàn)。需要更換讀頭順序時(shí)可調(diào)整Engine.RemoteIPHeaders默認(rèn)為[X-Forwarded-For, X-Real-IP]ClientIP()按該列表順序逐個(gè)嘗試取第一個(gè)能解析出有效 IP 的頭。配置完成后核對(duì)點(diǎn)就是兩個(gè)啟動(dòng)日志無 trust-all 警告handler 打印的ClientIP:與代理鏈中的真實(shí)來源一致。更多細(xì)節(jié)可回到 docs/doc.md 的 Dont trust all proxies 一節(jié)?!久赓M(fèi)下載鏈接】ginGin is a high-performance HTTP web framework written in Go. It provides a Martini-like API but with significantly better performance—up to 40 times faster—thanks to httprouter. Gin is designed for building REST APIs, web applications, and microservices.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/gi/gin創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考