- 48 个 Go 文件所有注释(行注释/块注释/行尾注释,含 _test.go)翻译为中文 - 保留技术标识符:SECURITY_TODO(n)、unsafe-inline、sqlite/mysql、路由参数等 - 代码、字符串字面量、日志消息保持英文原文,零逻辑改动 - go build/vet 通过,go test -count=1 ./... 全绿
60 lines
1.8 KiB
Go
60 lines
1.8 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// newClientIPRouter 模拟生产环境的可信代理配置:
|
|
// 仅信任回环地址(即 Caddy/nginx 主机)。
|
|
func newClientIPRouter() *gin.Engine {
|
|
gin.SetMode(gin.TestMode)
|
|
r := gin.New()
|
|
if err := r.SetTrustedProxies([]string{"127.0.0.1", "::1"}); err != nil {
|
|
panic(err)
|
|
}
|
|
r.GET("/ip", func(c *gin.Context) {
|
|
c.String(http.StatusOK, c.ClientIP())
|
|
})
|
|
return r
|
|
}
|
|
|
|
func TestClientIPSpoofingBlocked(t *testing.T) {
|
|
r := newClientIPRouter()
|
|
|
|
// 直接(不受信任)客户端伪造 X-Forwarded-For 时,
|
|
// 不得改变记录的 IP。
|
|
req := httptest.NewRequest(http.MethodGet, "/ip", nil)
|
|
req.RemoteAddr = "203.0.113.5:12345"
|
|
req.Header.Set("X-Forwarded-For", "6.6.6.6")
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
if got := w.Body.String(); got != "203.0.113.5" {
|
|
t.Errorf("direct client with forged XFF: got %q, want 203.0.113.5", got)
|
|
}
|
|
|
|
// 可信代理(回环)转发真实链路:最右侧不受信任的条目生效,
|
|
// 更早的(客户端提供的)条目被忽略。
|
|
req = httptest.NewRequest(http.MethodGet, "/ip", nil)
|
|
req.RemoteAddr = "127.0.0.1:54321"
|
|
req.Header.Set("X-Forwarded-For", "6.6.6.6, 198.51.100.42")
|
|
w = httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
if got := w.Body.String(); got != "198.51.100.42" {
|
|
t.Errorf("proxy-forwarded chain: got %q, want 198.51.100.42 (client-supplied entry must be ignored)", got)
|
|
}
|
|
|
|
// 可信代理转发单个条目:该条目即为客户端。
|
|
req = httptest.NewRequest(http.MethodGet, "/ip", nil)
|
|
req.RemoteAddr = "127.0.0.1:54321"
|
|
req.Header.Set("X-Forwarded-For", "198.51.100.42")
|
|
w = httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
if got := w.Body.String(); got != "198.51.100.42" {
|
|
t.Errorf("proxy-forwarded single entry: got %q, want 198.51.100.42", got)
|
|
}
|
|
}
|