feat(security): 增强访问控制安全性,支持可信代理配置
- 新增 TrustedProxies 配置项,安全解析 X-Forwarded-For - 防止 IP 伪造攻击,仅信任来自可信代理的头部 - 使用右侧非可信 IP 作为真实客户端 IP - 改进连接数限制中间件集成 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
ae8ea1ce0c
commit
98736e1f1c
@ -146,9 +146,10 @@ type SecurityConfig struct {
|
||||
|
||||
// AccessConfig IP 访问控制配置。
|
||||
type AccessConfig struct {
|
||||
Allow []string `yaml:"allow"` // 允许的 IP/CIDR 列表
|
||||
Deny []string `yaml:"deny"` // 拒绝的 IP/CIDR 列表
|
||||
Default string `yaml:"default"` // 默认动作:allow 或 deny
|
||||
Allow []string `yaml:"allow"` // 允许的 IP/CIDR 列表
|
||||
Deny []string `yaml:"deny"` // 拒绝的 IP/CIDR 列表
|
||||
Default string `yaml:"default"` // 默认动作:allow 或 deny
|
||||
TrustedProxies []string `yaml:"trusted_proxies"` // 可信代理 CIDR 列表,用于 X-Forwarded-For 解析
|
||||
}
|
||||
|
||||
// RateLimitConfig 速率限制配置。
|
||||
|
||||
@ -59,6 +59,9 @@ type AccessControl struct {
|
||||
// defaultAction 默认操作,当无规则匹配时执行
|
||||
defaultAction Action
|
||||
|
||||
// trustedProxies 可信代理 CIDR 列表,用于安全解析 X-Forwarded-For
|
||||
trustedProxies []net.IPNet
|
||||
|
||||
// mu 保护并发访问的读写锁
|
||||
mu sync.RWMutex
|
||||
}
|
||||
@ -98,6 +101,15 @@ func NewAccessControl(cfg *config.AccessConfig) (*AccessControl, error) {
|
||||
ac.denyList = append(ac.denyList, *network)
|
||||
}
|
||||
|
||||
// 解析可信代理列表
|
||||
for _, cidr := range cfg.TrustedProxies {
|
||||
network, err := parseCIDR(cidr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid trusted_proxy CIDR %s: %w", cidr, err)
|
||||
}
|
||||
ac.trustedProxies = append(ac.trustedProxies, *network)
|
||||
}
|
||||
|
||||
// 设置默认操作
|
||||
switch strings.ToLower(cfg.Default) {
|
||||
case "allow", "":
|
||||
@ -130,7 +142,7 @@ func (ac *AccessControl) Name() string {
|
||||
// - fasthttp.RequestHandler: 包装后的处理器
|
||||
func (ac *AccessControl) Process(next fasthttp.RequestHandler) fasthttp.RequestHandler {
|
||||
return func(ctx *fasthttp.RequestCtx) {
|
||||
clientIP := getClientIP(ctx)
|
||||
clientIP := ac.getClientIP(ctx)
|
||||
|
||||
// 检查访问权限
|
||||
if !ac.Check(clientIP) {
|
||||
@ -287,37 +299,83 @@ func parseCIDR(cidr string) (*net.IPNet, error) {
|
||||
return network, nil
|
||||
}
|
||||
|
||||
// getClientIP 从请求上下文提取客户端 IP。
|
||||
// getClientIP 从请求上下文安全提取客户端 IP。
|
||||
//
|
||||
// 按优先级依次检查:X-Forwarded-For、X-Real-IP、RemoteAddr。
|
||||
// 仅当请求来自可信代理时,才解析 X-Forwarded-For 头部。
|
||||
// 使用右侧(最接近客户端)的非可信 IP 作为真实客户端 IP。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: FastHTTP 请求上下文
|
||||
//
|
||||
// 返回值:
|
||||
// - net.IP: 客户端 IP 地址,无法获取时返回 nil
|
||||
func getClientIP(ctx *fasthttp.RequestCtx) net.IP {
|
||||
// 优先检查 X-Forwarded-For 头部
|
||||
if xff := ctx.Request.Header.Peek("X-Forwarded-For"); len(xff) > 0 {
|
||||
ips := strings.Split(string(xff), ",")
|
||||
if len(ips) > 0 {
|
||||
ipStr := strings.TrimSpace(ips[0])
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip != nil {
|
||||
return ip
|
||||
func (ac *AccessControl) getClientIP(ctx *fasthttp.RequestCtx) net.IP {
|
||||
remoteIP := getRemoteAddrIP(ctx)
|
||||
|
||||
// 仅当配置了可信代理且请求来自可信代理时,才解析 X-Forwarded-For
|
||||
if len(ac.trustedProxies) > 0 && remoteIP != nil {
|
||||
isTrusted := false
|
||||
for _, network := range ac.trustedProxies {
|
||||
if network.Contains(remoteIP) {
|
||||
isTrusted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isTrusted {
|
||||
// 使用右侧(最接近客户端)的非可信 IP
|
||||
if xff := ctx.Request.Header.Peek("X-Forwarded-For"); len(xff) > 0 {
|
||||
ips := strings.Split(string(xff), ",")
|
||||
for i := len(ips) - 1; i >= 0; i-- {
|
||||
ipStr := strings.TrimSpace(ips[i])
|
||||
if ip := net.ParseIP(ipStr); ip != nil {
|
||||
// 检查该 IP 是否在可信代理列表中
|
||||
trusted := false
|
||||
for _, network := range ac.trustedProxies {
|
||||
if network.Contains(ip) {
|
||||
trusted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !trusted {
|
||||
return ip
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查 X-Real-IP 头部
|
||||
if xri := ctx.Request.Header.Peek("X-Real-IP"); len(xri) > 0 {
|
||||
ip := net.ParseIP(string(xri))
|
||||
if ip != nil {
|
||||
return ip
|
||||
// 检查 X-Real-IP 头部(仅来自可信代理时)
|
||||
if len(ac.trustedProxies) > 0 && remoteIP != nil {
|
||||
isTrusted := false
|
||||
for _, network := range ac.trustedProxies {
|
||||
if network.Contains(remoteIP) {
|
||||
isTrusted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isTrusted {
|
||||
if xri := ctx.Request.Header.Peek("X-Real-IP"); len(xri) > 0 {
|
||||
if ip := net.ParseIP(string(xri)); ip != nil {
|
||||
return ip
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 回退到 RemoteAddr
|
||||
return remoteIP
|
||||
}
|
||||
|
||||
// getRemoteAddrIP 从 RemoteAddr 提取 IP。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: FastHTTP 请求上下文
|
||||
//
|
||||
// 返回值:
|
||||
// - net.IP: 客户端 IP 地址,无法获取时返回 nil
|
||||
func getRemoteAddrIP(ctx *fasthttp.RequestCtx) net.IP {
|
||||
if addr := ctx.RemoteAddr(); addr != nil {
|
||||
if tcpAddr, ok := addr.(*net.TCPAddr); ok {
|
||||
return tcpAddr.IP
|
||||
@ -331,7 +389,6 @@ func getClientIP(ctx *fasthttp.RequestCtx) net.IP {
|
||||
ipStr = strings.TrimPrefix(strings.TrimSuffix(ipStr, "]"), "[")
|
||||
return net.ParseIP(ipStr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
package security
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -559,10 +560,11 @@ func HashPasswordBcrypt(password string, cost int) (string, error) {
|
||||
// - string: Argon2id 哈希字符串
|
||||
// - error: 生成失败时返回错误
|
||||
func HashPasswordArgon2id(password string, params argon2Params) (string, error) {
|
||||
// 生成随机盐值
|
||||
// 使用加密安全的随机数生成盐值
|
||||
salt := make([]byte, params.saltLen)
|
||||
// 注意:生产环境应使用 crypto/rand 生成盐值
|
||||
// 此工具函数使用占位符方法
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return "", fmt.Errorf("failed to generate salt: %w", err)
|
||||
}
|
||||
|
||||
// 生成哈希
|
||||
hash := argon2.IDKey([]byte(password), salt,
|
||||
|
||||
@ -35,6 +35,7 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
@ -602,14 +603,13 @@ func (m *connLimiterMiddleware) Process(next fasthttp.RequestHandler) fasthttp.R
|
||||
}
|
||||
}
|
||||
|
||||
// 连接数原子操作辅助函数
|
||||
// 连接数原子操作辅助函数
|
||||
func loadInt64(ptr *int64) int64 {
|
||||
return *ptr // 生产环境应使用 sync/atomic
|
||||
return atomic.LoadInt64(ptr)
|
||||
}
|
||||
|
||||
func addInt64(ptr *int64, delta int64) {
|
||||
*ptr += delta // 简化实现;生产环境应使用 atomic.AddInt64
|
||||
atomic.AddInt64(ptr, delta)
|
||||
}
|
||||
|
||||
// 验证接口实现
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user