Gin 中间件安全控制
中间件是 Gin 安全控制的核心环节,统一处理请求验证、安全头部、跨域控制等安全措施。
安全头部中间件
Go
func SecurityHeadersMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 防止 XSS 攻击
c.Header("X-XSS-Protection", "1; mode=block")
// 防止 MIME 类型嗅探
c.Header("X-Content-Type-Options", "nosniff")
// 防止点击劫持
c.Header("X-Frame-Options", "DENY")
// 强制 HTTPS(生产环境启用)
c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
// 内容安全策略
c.Header("Content-Security-Policy", "default-src 'self'")
// 引用策略
c.Header("Referrer-Policy", "strict-origin-when-cross-origin")
// 权限策略
c.Header("Permissions-Policy", "geolocation=(), microphone=()")
c.Next()
}
}
func main() {
r := gin.Default()
r.Use(SecurityHeadersMiddleware())
r.Run(":8080")
}
CORS 跨域控制
基础 CORS 配置
Go
func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
c.Header("Access-Control-Expose-Headers", "Content-Length")
c.Header("Access-Control-Allow-Credentials", "true")
c.Header("Access-Control-Max-Age", "86400")
// 处理预检请求
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}
严格的 CORS 配置
Go
func StrictCORSMiddleware(allowedOrigins []string) gin.HandlerFunc {
return func(c *gin.Context) {
origin := c.GetHeader("Origin")
// 检查是否为允许的来源
allowed := false
for _, o := range allowedOrigins {
if o == origin || o == "*" {
allowed = true
break
}
}
if !allowed && origin != "" {
c.AbortWithStatus(403)
return
}
if allowed {
c.Header("Access-Control-Allow-Origin", origin)
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With")
c.Header("Access-Control-Allow-Credentials", "true")
c.Header("Access-Control-Max-Age", "3600")
}
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}
// 使用
r.Use(StrictCORSMiddleware([]string{
"https://example.com",
"https://admin.example.com",
}))
请求验证中间件
请求大小限制
Go
func RequestSizeLimitMiddleware(maxSize int64) gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.ContentLength > maxSize {
c.JSON(413, gin.H{"error": "请求体过大"})
c.Abort()
return
}
// 限制实际读取大小
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, maxSize)
c.Next()
}
}
// 使用:限制请求体最大 1MB
r.Use(RequestSizeLimitMiddleware(1 << 20))
Content-Type 验证
Go
func ContentTypeMiddleware(allowedTypes []string) gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.Method == "GET" || c.Request.Method == "DELETE" {
c.Next()
return
}
contentType := c.GetHeader("Content-Type")
valid := false
for _, t := range allowedTypes {
if strings.Contains(contentType, t) {
valid = true
break
}
}
if !valid {
c.JSON(415, gin.H{"error": "不支持的 Content-Type"})
c.Abort()
return
}
c.Next()
}
}
// 使用:只允许 JSON
r.Use(ContentTypeMiddleware([]string{"application/json"}))
敏感信息过滤
响应信息清理
Go
func ResponseFilterMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 记录原始响应
blw := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
c.Writer = blw
c.Next()
// 过滤敏感字段(仅对 JSON 响应)
if strings.Contains(c.Writer.Header().Get("Content-Type"), "application/json") {
filtered := filterSensitiveFields(blw.body.String())
c.Writer = gin.ResponseWriter(blw.original)
c.Writer.Write([]byte(filtered))
}
}
}
type bodyLogWriter struct {
body *bytes.Buffer
ResponseWriter gin.ResponseWriter
original gin.ResponseWriter
}
func filterSensitiveFields(jsonStr string) string {
// 移除密码、token 等敏感字段
sensitiveFields := []string{"password", "token", "secret", "api_key"}
var data map[string]interface{}
json.Unmarshal([]byte(jsonStr), &data)
for _, field := range sensitiveFields {
if _, exists := data[field]; exists {
data[field] = "[FILTERED]"
}
}
result, _ := json.Marshal(data)
return string(result)
}
日志敏感信息过滤
Go
func SensitiveLogMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 记录请求(过滤敏感参数)
logRequest(c)
c.Next()
// 记录响应
logResponse(c)
}
}
func logRequest(c *gin.Context) {
params := c.Request.URL.Query()
filteredParams := make(map[string]string)
sensitiveKeys := []string{"password", "token", "key", "secret"}
for k, v := range params {
if isSensitiveKey(k, sensitiveKeys) {
filteredParams[k] = "[FILTERED]"
} else {
filteredParams[k] = strings.Join(v, ",")
}
}
log.Printf("Request: %s %s Params: %v", c.Request.Method, c.Request.URL.Path, filteredParams)
}
请求来源验证
IP 白名单
Go
func IPWhitelistMiddleware(allowedIPs []string) gin.HandlerFunc {
allowedSet := make(map[string]bool)
for _, ip := range allowedIPs {
allowedSet[ip] = true
}
return func(c *gin.Context) {
clientIP := c.ClientIP()
if !allowedSet[clientIP] {
c.JSON(403, gin.H{"error": "IP 不在白名单中"})
c.Abort()
return
}
c.Next()
}
}
// 使用
r.Use(IPWhitelistMiddleware([]string{"127.0.0.1", "192.168.1.100"}))
Referer 验证
Go
func RefererCheckMiddleware(allowedReferers []string) gin.HandlerFunc {
return func(c *gin.Context) {
referer := c.GetHeader("Referer")
// 对特定接口检查来源
if strings.HasPrefix(c.Request.URL.Path, "/api/admin") {
valid := false
for _, r := range allowedReferers {
if strings.HasPrefix(referer, r) {
valid = true
break
}
}
if !valid {
c.JSON(403, gin.H{"error": "非法请求来源"})
c.Abort()
return
}
}
c.Next()
}
}
综合安全中间件
Go
func FullSecurityMiddleware(config SecurityConfig) gin.HandlerFunc {
return func(c *gin.Context) {
// 1. 请求大小检查
if c.Request.ContentLength > config.MaxRequestSize {
c.JSON(413, gin.H{"error": "请求过大"})
c.Abort()
return
}
// 2. IP 白名单检查
if len(config.IPWhitelist) > 0 {
ip := c.ClientIP()
if !contains(config.IPWhitelist, ip) {
c.JSON(403, gin.H{"error": "禁止访问"})
c.Abort()
return
}
}
// 3. Content-Type 验证
if c.Request.Method != "GET" && c.Request.Method != "DELETE" {
ct := c.GetHeader("Content-Type")
if !isValidContentType(ct, config.AllowedContentTypes) {
c.JSON(415, gin.H{"error": "Content-Type 无效"})
c.Abort()
return
}
}
// 4. 设置安全头部
setSecurityHeaders(c)
c.Next()
}
}
type SecurityConfig struct {
MaxRequestSize int64
IPWhitelist []string
AllowedContentTypes []string
AllowedOrigins []string
}
安全中间件配置示例
Go
func main() {
r := gin.Default()
// 安全配置
config := SecurityConfig{
MaxRequestSize: 2 << 20, // 2MB
IPWhitelist: []string{},
AllowedContentTypes: []string{"application/json", "multipart/form-data"},
AllowedOrigins: []string{"https://example.com"},
}
// 应用安全中间件
r.Use(FullSecurityMiddleware(config))
r.Use(StrictCORSMiddleware(config.AllowedOrigins))
r.Use(SecurityHeadersMiddleware())
// 路由配置
api := r.Group("/api")
api.Use(JWTAuthMiddleware())
{
api.GET("/data", GetData)
}
r.Run(":8080")
}
安全头部说明
| 头部 | 作用 | 推荐值 |
|---|---|---|
| X-XSS-Protection | XSS 过滤 | 1; mode=block |
| X-Content-Type-Options | MIME 嗅探防护 | nosniff |
| X-Frame-Options | 点击劫持防护 | DENY |
| Strict-Transport-Security | 强制 HTTPS | max-age=31536000 |
| Content-Security-Policy | 内容安全策略 | default-src 'self' |
注意:CORS 配置需严格限制允许的来源,避免使用
*允许所有域名。
要点总结
- 安全头部:设置 XSS、点击劫持、HTTPS 强制等防护头
- CORS 控制:严格限制允许的域名和方法
- 请求验证:限制大小、验证 Content-Type
- 敏感过滤:日志和响应中隐藏敏感信息
- 来源验证:IP 白名单、Referer 检查
- 综合配置:根据业务场景组合多个安全措施
📝 发现内容有误?点击此处直接编辑