feat: 添加安全中间件、优化限流器与API密钥验证

1. 新增安全响应头中间件强化请求安全性
2. 使用恒定时间比较修复API密钥验证时序漏洞
3. 新增限流器条目上限配置与自动清理逻辑
4. 优化CORS日志与客户端IP获取逻辑
5. 升级Go构建环境与基础镜像版本
6. 新增非root用户运行容器增强安全性
This commit is contained in:
sun
2026-05-24 15:33:27 +08:00
parent 9c35f780db
commit 9b2a1d1531
8 changed files with 53 additions and 13 deletions
+2 -1
View File
@@ -1,6 +1,7 @@
package middleware
import (
"crypto/subtle"
"encoding/json"
"log"
"net/http"
@@ -39,7 +40,7 @@ func ValidateAPIKey(r *http.Request) bool {
token := strings.TrimPrefix(authHeader, "Bearer ")
for _, validKey := range validAPIKeys {
if token == validKey {
if subtle.ConstantTimeCompare([]byte(token), []byte(validKey)) == 1 {
return true
}
}
+3 -6
View File
@@ -96,16 +96,13 @@ func CORS(next http.Handler) http.Handler {
} else if !strings.Contains(vary, "Origin") {
w.Header().Set("Vary", vary+", Origin")
}
} else {
log.Printf("CORS拦截: 来源=%q 路径=%s 方法=%s 客户端=%s",
origin, r.URL.Path, r.Method, GetClientIP(r))
}
}
if r.Method == http.MethodOptions {
if origin != "" {
if _, matched := matchOrigin(origin); !matched {
w.WriteHeader(http.StatusNoContent)
return
}
}
w.WriteHeader(http.StatusNoContent)
return
}
+16 -2
View File
@@ -1,6 +1,7 @@
package middleware
import (
"log"
"net"
"net/http"
"strings"
@@ -77,6 +78,16 @@ func (rl *RateLimiter) cleanup() {
rl.requests[k] = valid
}
}
if len(rl.requests) > common.MaxRateLimiterEntries {
log.Printf("警告: 限流器条目数 %d 超过上限 %d,触发强制清理", len(rl.requests), common.MaxRateLimiterEntries)
for k := range rl.requests {
if len(rl.requests) <= common.MaxRateLimiterEntries/2 {
break
}
delete(rl.requests, k)
}
}
}
func GetClientIP(r *http.Request) string {
@@ -84,11 +95,14 @@ func GetClientIP(r *http.Request) string {
if xForwardedFor != "" {
ips := strings.Split(xForwardedFor, ",")
if len(ips) > 0 {
return strings.TrimSpace(ips[0])
ip := strings.TrimSpace(ips[0])
if ip != "" {
return ip
}
}
}
xRealIP := r.Header.Get("X-Real-IP")
xRealIP := strings.TrimSpace(r.Header.Get("X-Real-IP"))
if xRealIP != "" {
return xRealIP
}
+21
View File
@@ -0,0 +1,21 @@
package middleware
import (
"net/http"
"strings"
)
func SecurityHeaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-Frame-Options", "DENY")
w.Header().Set("X-XSS-Protection", "1; mode=block")
w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
if strings.HasPrefix(r.URL.Path, "/v1/") || r.URL.Path == "/health" {
w.Header().Set("Cache-Control", "no-store")
}
next.ServeHTTP(w, r)
})
}