feat: 添加安全中间件、优化限流器与API密钥验证
1. 新增安全响应头中间件强化请求安全性 2. 使用恒定时间比较修复API密钥验证时序漏洞 3. 新增限流器条目上限配置与自动清理逻辑 4. 优化CORS日志与客户端IP获取逻辑 5. 升级Go构建环境与基础镜像版本 6. 新增非root用户运行容器增强安全性
This commit is contained in:
+2
-1
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user