middleware

package
v0.33.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 4, 2026 License: Apache-2.0 Imports: 28 Imported by: 0

Documentation

Overview

auditactor.go 审计操作者提取:从请求上下文取 (用户ID, 用户名, IP)。

此前该逻辑以 actorFromCtx 内嵌于 admin 域;config 域独立后两个域都要写 管理操作审计(admin_operation_logs 同表),提取逻辑下沉 pkg 层单源 (AuthMiddleware 写入的身份 value 本就由本包提供)。

Package middleware 提供 HTTP 中间件。

本文件实现 Prometheus 指标采集中间件,暴露 HTTP RED 指标:

  • RequestsTotal(Counter):请求总数,按 method/path/status 打标
  • RequestDurationSeconds(Histogram):请求延迟分布
  • RequestsInFlight(Gauge):当前处理中的请求数

指标通过 /metrics 端点暴露(由 bootstrap customizedRegister 注册 promhttp.Handler)。

Package middleware 提供 Hertz 中间件:

  • RateLimit: IP 维度 + 接口维度双层限流

实现:默认进程内 token bucket——per-IP 桶由 oekit/concurrency/ratelimit 的 KeyedLimiter 托管(工厂造桶 + lastSeen TTL 自动清死桶,底层 x/time/rate); 配置 rate_limit.use_redis=true 且 Redis 可用时切换为 Redis 固定窗口计数 (多实例共享)。命中限流后按 block_seconds 封禁该 IP+维度(封禁同样 Redis/内存双写)。配置可经 /admin/ratelimit/config 运行时热更。

Index

Constants

View Source
const (
	// ScopeAdmin 管理面(/admin/**、/api/v1/admin/**)。
	ScopeAdmin = "admin"
	// ScopeUser 用户自有资源(/user/**、/api/v1/user/**)。
	ScopeUser = "user"
	// ScopeShare 寄件与取件(/share/**、/chunk/**、/anonymous/** 等)。
	ScopeShare = "share"
)

===== API Key 权限范围(scopes,2026-10-30)=====

用户级 API Key 此前是**全量令牌**:挂到 MCP / CI / 集成方即全权, 无法「只给寄件权限」。本次给 Key 加「权限范围」:创建时指定该 Key 被允许的 能力面,服务端按**请求路径**判定所需能力,缺该能力 ⇒ 403。

**缺省 / 空 = 全量** ⇒ 与本机制引入前行为完全一致(老 Key 无需重建、存量部署零影响)。

View Source
const (
	// SessionCookieName 会话 Cookie 名(host-only,不设 Domain)
	SessionCookieName = "pb_token"
	// CSRFHeaderName Cookie 认证下非安全方法的必备请求头
	CSRFHeaderName = "X-Requested-With"
	// CSRFHeaderValue 该请求头的期望值
	CSRFHeaderValue = "XMLHttpRequest"
)
View Source
const TraceIDHeader = "X-Trace-Id"

TraceIDHeader 请求链路 ID 透传 header,与 internal/pkg/resp 对齐。

Variables

View Source
var AllAPIKeyScopes = []string{ScopeAdmin, ScopeUser, ScopeShare}

AllAPIKeyScopes 全部合法取值(管理台下拉的数据源;校验也用它)。

Functions

func APIKeyIDFromContext

func APIKeyIDFromContext(ctx context.Context) (uint, bool)

APIKeyIDFromContext 从 ctx 读取认证所用的 API Key ID(JWT/匿名请求返回 false)。

func APIKeyScopeAllowed added in v0.32.31

func APIKeyScopeAllowed(scopes []string, need string) bool

APIKeyScopeAllowed 判断该 Key 是否被允许访问所需能力面。 scopes 为空 = 全量(老 Key / 未指定范围的 Key)。

func AccessLog

func AccessLog() app.HandlerFunc

AccessLog 结构化访问日志中间件。

记录每个请求的 method/path/status/ip/latency/trace_id,便于生产环境排障。 与 RequestID 中间件配合:trace_id 由 RequestID 写入 ctx 与 RequestContext, 本中间件从 RequestContext 读取并打入日志字段,实现日志与 resp envelope 的 trace_id 一致,方便串联请求链路。

func AdminMiddleware

func AdminMiddleware() app.HandlerFunc

AdminMiddleware 管理员权限中间件

实现(2026-10-05 审计 P0 修复):认证+授权必须在本中间件内联完成后统一放行。 此前复用 AuthMiddleware()——其成功路径会 c.Next 把链上剩余 handler(含业务 handler)先执行完,返回后才做角色检查:写操作已生效、读操作的响应体已生成 (hertz 的 JSON 写入是追加语义,403 只会拼在业务响应后面)。任意注册用户 因此可执行全部 /admin/* 与 /api/v1/mcp。

func AdminOrAdminKey added in v0.20.0

func AdminOrAdminKey() app.HandlerFunc

AdminOrAdminKey 管理面自动化通道(2026-10-14 P3):admin JWT(会话/Bearer) 或 **admin 用户**的 API Key(ApiKey 头 / Bearer pbk_* / X-API-Key)。 与 UserOrAPIKey 边界(本文件 §4.1「绝不挂 /admin」)的区分:本中间件的 Key 路径强制 admin 角色复核——非管理员的 Key 一律 403;唯一消费方是 pb CLI 的 直传自动化(/admin/p2p/* 组),不开普通 Key 进管理面的口子。

func AuditActor added in v0.15.1

func AuditActor(ctx context.Context) (id *uint, name, ip string)

AuditActor 从请求上下文提取审计操作者:

  • id: 登录用户 ID(未登录为 nil)
  • name: 用户名;定时任务等非请求场景无身份,记为 "system"
  • ip: 可信代理解析后的客户端 IP

func AuthMiddleware

func AuthMiddleware() app.HandlerFunc

AuthMiddleware JWT认证中间件

令牌来源:Authorization: Bearer 头或会话 Cookie(pb_token,浏览器端默认; Cookie 认证的非安全方法另有 CSRF 头门禁,见 session.go)。

func CSRFAllowed

func CSRFAllowed(c *app.RequestContext, viaCookie bool) bool

CSRFAllowed Cookie 认证的 CSRF 门禁:安全方法(GET/HEAD/OPTIONS)放行; 非安全方法要求携带自定义头 X-Requested-With: XMLHttpRequest。

func ClearSessionCookie

func ClearSessionCookie(c *app.RequestContext)

ClearSessionCookie 清除会话 Cookie(logout/会话失效时调用)。

func ClientIP

func ClientIP(c *app.RequestContext) string

ClientIP 解析客户端真实 IP(可信代理解析,XFF 从右向左)。

规则:

  1. 直连地址不在可信代理网段 → 返回直连地址(忽略一切代理头)
  2. 直连地址可信 → 从 X-Forwarded-For 最右端向左扫描, 跳过可信代理地址,返回第一个不可信地址(即真实客户端)
  3. XFF 缺失或整条链都没有合法候选 → 返回直连地址

**不采信 X-Real-IP**:单跳头无法区分「代理写的」与「客户端自带的」——网关 默认不写该头但会原样透传客户端自带的,采信它等于让客户端任意改写自己的 客户端 IP(重置匿名配额 / 改写审计归因)。上游 oekit/pkg v0.26.0 起默认 同样不采信(oekit/pkg#106)。

func ClientIPFromContext

func ClientIPFromContext(ctx context.Context) string

ClientIPFromContext 从 ctx 读取已解析的客户端 IP

func FormatLockKey

func FormatLockKey(parts ...string) string

FormatLockKey 组合锁定维度键(值做转义避免键冲突)

func IdentityFresh added in v0.14.9

func IdentityFresh(ctx context.Context, claims *auth.Claims) bool

IdentityFresh 导出的身份复核入口:AuthMiddleware 之外的认证路径 (如 transport 层 UserAuth)共享同一 30s 缓存回查——封禁/降权/改密后 旧 token 不再在这些路由越界可用(2026-10-08 加固)。

func InvalidateIdentity

func InvalidateIdentity(userID uint)

InvalidateIdentity 清除某用户的回查缓存(改密/封禁/降权落库后调用, 使本次变更绕过 TTL 立即生效)。

func LookupLockKey added in v0.14.7

func LookupLockKey(scope, ip, code string) string

LookupLockKey 取件/分享码查询的失败锁定键:查询折叠开启(download. code_case_insensitive,默认开)时把 code 归一为大写,使 abc/ABC 等大小写 变体共享同一失败计数,防变体绕过 IP+code 维度爆破锁定。

func MetricsMiddleware

func MetricsMiddleware() app.HandlerFunc

MetricsMiddleware 采集 HTTP RED 指标的中间件。 必须在 NewMetrics() 之后、路由注册之前通过 h.Use 注册为全局中间件。

func OptionalAPIKey

func OptionalAPIKey() app.HandlerFunc

OptionalAPIKey 可选 API Key 认证(fail-closed:携带即校验,无效一律 401/429)

func OptionalAuthMiddleware

func OptionalAuthMiddleware() app.HandlerFunc

OptionalAuthMiddleware 可选认证中间件(不强制要求登录)

func OptionalIdentity

func OptionalIdentity() []app.HandlerFunc

OptionalIdentity 可选身份认证组:JWT(既有可选语义)→ API Key(fail-closed)。 未携带凭证 → 匿名;携带 Key 但无效 → 401;JWT 无效 → 匿名(会话过期 UX,与 Key 有意不对称)。 供 gen 路由挂载点整体替换 OptionalAuthMiddleware()(挂载函数直接 return 本切片)。

func Recovery

func Recovery() app.HandlerFunc

Recovery 全局 panic 恢复中间件。

捕获 handler 链中任何 panic,记录堆栈与 trace_id,并返回统一 500 响应, 防止单个请求的 panic 导致整个进程崩溃(Hertz 默认不保证全链路 recover)。

必须注册在其他中间件之前,以覆盖后续所有 handler。

func RequestID

func RequestID() app.HandlerFunc

RequestID 为每个请求生成 / 透传 trace_id(X-Trace-Id)。

行为:

  • 沿用请求头 X-Trace-Id(须通过白名单校验:长度 ≤64、字符集 [A-Za-z0-9._-],见 resp.ValidTraceID。2026-10-06 审计:原样回显 任意值会把超长串写进响应头并落入访问日志——日志刷量/注入面)
  • 缺失或不合规时生成 UUID
  • 写入响应头、app.RequestContext(Set)与 context.Context, 使访问日志 / 业务日志 / resp envelope 都能拿到同一个 trace_id。

与 resp.getOrGenTraceID 配合:resp 包在构造响应体时会读取同样的 header(同样过白名单),保证全链路 trace_id 一致。

func RequiredAPIKeyScope added in v0.32.31

func RequiredAPIKeyScope(path string) string

RequiredAPIKeyScope 返回该路径所需的权限范围(空串 = 不要求该能力)。

func ResolveClientIP

func ResolveClientIP(remote, xff string) string

ResolveClientIP 可信代理解析核心(纯函数,便于单测覆盖伪造场景)。 remote 为直连对端地址(host:port 或裸 IP);xff 为原始代理头。

采信判定与 XFF 可信链扫描收编上游 oekit/pkg/convert.ResolveClientIP (v0.25.2 随 #95 落地,v0.26.0 随 #104/#106/#107 收紧)——与 bianque 等 消费方共用同一份实现,避免各仓各写一遍后口径漂移。

本仓此前自持的两处加固已由上游补齐,本地不再重复实现:

  1. 代理头候选值合法性校验——任意字符串进限流/锁定键与审计日志,既是 「伪造 IP 永不命中计数 = 绕过」,也可注入任意内容污染日志;
  2. 候选值带端口时归一为裸 IP——同一客户端换个端口就能换出一个新桶。

空对端地址返回 "unknown"(本仓约定:调用方按字符串拼进限流/锁定键, 空串会让"未知来源"与"真实空值"混在一起)。

func RoleFromContext

func RoleFromContext(ctx context.Context) string

RoleFromContext 从 ctx 读取角色

func SecurityHeaders

func SecurityHeaders() app.HandlerFunc

SecurityHeaders 安全响应头中间件。

func SessionToken

func SessionToken(c *app.RequestContext) (string, bool)

SessionToken 提取会话令牌:Authorization: Bearer 头优先,回退会话 Cookie。 返回 (token, viaCookie)。

func SetAPIKeyStore

func SetAPIKeyStore(s APIKeyStore)

SetAPIKeyStore 注入持久化实现(bootstrap 装配调用)。

func SetIdentityHeaders

func SetIdentityHeaders(c *app.RequestContext, claims *auth.Claims)

SetIdentityHeaders 身份响应头三连(X-User-ID/X-Username/X-Role)。

func SetIdentityLoader

func SetIdentityLoader(fn func(ctx context.Context, userID uint) (*IdentityRecord, error))

SetIdentityLoader 注入身份加载器(bootstrap 调用一次;nil = 关闭回查, 退化为纯 JWT 语义——单测/轻量环境)。

func SetJWTClaims

func SetJWTClaims(c *app.RequestContext, claims *auth.Claims)

SetJWTClaims JWT 身份 c.Set 四件套(键名单一真相源:user_id/username/role/auth_type, 与 transport 侧 ContextKey* 常量同字符串)。pkg 与 transport 两个中间件栈均委托此处。

func SetSecurityHeadersConfig

func SetSecurityHeadersConfig(cfg SecurityHeadersConfig)

SetSecurityHeadersConfig 设置安全头全局配置(bootstrap 调用)。

func SetSessionCookie

func SetSessionCookie(c *app.RequestContext, token string, maxAgeSeconds int)

SetSessionCookie 下发会话 Cookie(maxAgeSeconds<=0 视为会话期 Cookie)。

func SetTrustedProxies

func SetTrustedProxies(cidrs []string) error

SetTrustedProxies 配置可信代理网段(CIDR 或单 IP)。 空列表 = 不信任任何代理头(直连部署的安全默认)。

条目解析收编上游 oekit/pkg/convert.TrustedProxySet(pkg v0.26.0):

  • 裸 IP 归一为单主机网段(IPv4 /32、IPv6 /128);
  • 非法条目**构造期报错**,不静默跳过——静默跳过把「配置写错」变成 「安全策略降级」且不可观测(此前裸 IP 被直接 continue 掉:配置不生效 且无任何告警,oekit/pkg#104)。

解析在构造期一次完成,请求期只做网段包含判定——此前每请求都要把整份清单 逐条 ParseCIDR 一遍(oekit/pkg#107)。

func TraceIDFromContext

func TraceIDFromContext(ctx context.Context) string

TraceIDFromContext 从 context.Context 提取 trace_id(service / 日志用)。 委托 logger 同名函数(真相源),保留本包导出兼容既有调用点。

func UserIDFromContext

func UserIDFromContext(ctx context.Context) (uint, bool)

UserIDFromContext 从 ctx 读取用户 ID(未认证返回 false)

func UserOrAPIKey

func UserOrAPIKey() app.HandlerFunc

UserOrAPIKey 登录凭证二选一:API Key 或 JWT(含黑名单检查,复用 AuthMiddleware 完整语义)。 供 /api/v1 自定义 REST 组替换 transport UserAuth,向第三方开放 Key 访问。 边界(设计文档 §4.1):绝不挂 /admin;/user/api-keys 保持 JWT-only(Key 不能管 Key)。

func UsernameFromContext

func UsernameFromContext(ctx context.Context) string

UsernameFromContext 从 ctx 读取用户名(未认证返回空串)

func ValidateAPIKeyScopes added in v0.32.31

func ValidateAPIKeyScopes(scopes []string) ([]string, error)

ValidateAPIKeyScopes 校验并归一化权限范围(去空白/去重;空 = 全量;非法值报错)。

非法值**直接报错**而不是悄悄忽略:用户以为自己签发的是受限 Key, 实际拿到全量 ⇒ 安全语义被静默放宽(与「旗标缺值被吞」同类事故)。

Types

type APIKeyPrincipal

type APIKeyPrincipal struct {
	KeyID    uint
	UserID   uint
	Username string
	Role     string
	// Scopes 该 Key 被允许的能力面(空 = **全量**,与本机制引入前一致)。
	Scopes []string
}

APIKeyPrincipal 认证所需的最小身份事实(user_api_keys × users 两表提炼, pkg 不直接引用 repo 模型类型)。

type APIKeyStore

type APIKeyStore interface {
	FindActiveByHash(ctx context.Context, keyHash string) (*APIKeyPrincipal, error)
	TouchLastUsed(ctx context.Context, keyID uint, ip string) error
}

APIKeyStore API Key 认证所需持久化能力(bootstrap 注入 dao 桥实现)。 语义约定:Key 不存在/已吊销/已过期/属主非 active 状态统一返回 error, 调用方不区分原因(防枚举)。

type CtxKeyTraceID

type CtxKeyTraceID = logger.CtxKeyTraceID

CtxKeyTraceID context.Context 中 trace_id 的键。

2026-10-16 起真相源下沉 pkg/logger(本包依赖 logger,反向 import 成环), 此处类型别名复用:类型同一性保证 WithValue/Value 配对,历史写入零迁移。

type IdentityRecord

type IdentityRecord struct {
	Status string
	Role   string
	Epoch  int
}

IdentityRecord 用户当前态快照(composition root 以 dao 桥注入)。

type LockedError

type LockedError struct {
	RemainingSeconds int
}

LockedError 被锁定错误(携带剩余秒数)

func (*LockedError) ErrCode

func (e *LockedError) ErrCode() int

ErrCode 转换为统一错误码

func (*LockedError) Error

func (e *LockedError) Error() string

type Lockout

type Lockout struct {
	// contains filtered or unexported fields
}

Lockout 登录/取件失败计数锁定(防爆破,非 QPS 语义)。

维度由调用方决定(建议 "ip"、"user:xxx"、"code:xxx" 组合键):

  • 窗口内失败次数达到 MaxAttempts → 锁定 LockSeconds
  • Redis 可用时计数/锁定共享(多实例生效);否则进程内存兜底 (2026-10-18 起兜底托管给 oekit concurrency v0.12.0 ratelimit.Lockout ——concurrency#21 落地物;滑动窗口/锁定条目免空闲回收/容量 fail-open 均为其契约能力,替代手搓 memHit/memFail+周期 GC 约 80 行)

func GetDefaultLockout

func GetDefaultLockout() *Lockout

GetDefaultLockout 获取全局锁定器(未初始化时用纯内存模式兜底,保证 handler 可用)

并发安全:先无锁读一次(快路径,绝大多数调用命中已初始化实例), 未初始化时再加写锁做 double-check 初始化。

func InitDefaultLockout

func InitDefaultLockout(rdb *redis.Client) *Lockout

InitDefaultLockout 初始化全局锁定器(bootstrap 调用一次)

兼作**测试重置**入口(传 nil 退回内存模式)⇒ 写操作受锁保护。

func NewLockout

func NewLockout(rdb *redis.Client) *Lockout

NewLockout 创建失败锁定器。rdb 为 nil 或配置未启用时仍返回可用实例(内存模式)。 nil 归一化:*redis.Client 的 typed-nil 赋给接口字段后 != nil 判真,会骗过 内存模式守卫并在方法调用时 panic——必须在注入边界处理。

func (*Lockout) CheckLocked

func (l *Lockout) CheckLocked(ctx context.Context, key string) (int, bool)

CheckLocked 返回 (剩余锁定秒数, 是否被锁)。未启用恒返回 (0,false)。

func (*Lockout) Enabled

func (l *Lockout) Enabled() bool

Enabled 是否启用

func (*Lockout) RecordFailure

func (l *Lockout) RecordFailure(ctx context.Context, key string) (bool, int)

RecordFailure 记录一次失败。返回 (是否触发锁定, 锁定秒数)。

func (*Lockout) Reset

func (l *Lockout) Reset(ctx context.Context, key string)

Reset 成功后清空失败计数。

type Metrics

type Metrics struct {
	// contains filtered or unexported fields
}

Metrics 指标容器。

func GetMetrics

func GetMetrics() *Metrics

GetMetrics 返回已注册的指标实例(未初始化返回 nil)。

func NewMetrics

func NewMetrics() *Metrics

NewMetrics 创建并向默认注册表注册指标。 调用一次即可;重复调用会 panic(prometheus 已注册)。

type RateLimitConfig

type RateLimitConfig struct {
	GlobalQPS    int  // 全局 IP 维度 QPS(每 IP)
	UploadQPS    int  // 上传接口 QPS
	DownloadQPS  int  // 下载接口 QPS
	LoginQPS     int  // 登录接口 QPS
	Burst        int  // 突发容量
	Enabled      bool // 总开关
	BlockSeconds int  // 触发限流后的封禁秒数(0 = 不封禁)
	UseRedis     bool // Redis 固定窗口模式(多实例共享)
}

RateLimitConfig 限流配置(运行时可调)

func DefaultRateLimitConfig

func DefaultRateLimitConfig() RateLimitConfig

DefaultRateLimitConfig 默认限流配置

func RateLimitConfigFromConf

func RateLimitConfigFromConf() RateLimitConfig

RateLimitConfigFromConf 从全局配置读取(未配置段时用默认值)

type RateLimiter

type RateLimiter struct {
	// contains filtered or unexported fields
}

RateLimiter 限流管理器

func GetDefaultRateLimiter

func GetDefaultRateLimiter() *RateLimiter

GetDefaultRateLimiter 获取默认限流器

func InitDefaultRateLimiter

func InitDefaultRateLimiter(cfg RateLimitConfig) *RateLimiter

InitDefaultRateLimiter 初始化默认限流器

func NewRateLimiter

func NewRateLimiter(cfg RateLimitConfig) *RateLimiter

NewRateLimiter 创建限流管理器

func (*RateLimiter) DownloadMiddleware

func (rl *RateLimiter) DownloadMiddleware() app.HandlerFunc

DownloadMiddleware 下载接口限流

func (*RateLimiter) GlobalMiddleware

func (rl *RateLimiter) GlobalMiddleware() app.HandlerFunc

GlobalMiddleware 全局限流中间件(用于所有接口)

func (*RateLimiter) LoginMiddleware

func (rl *RateLimiter) LoginMiddleware() app.HandlerFunc

LoginMiddleware 登录接口限流

func (*RateLimiter) Middleware

func (rl *RateLimiter) Middleware(s scope) app.HandlerFunc

Middleware 返回 Hertz 限流中间件

  • scope: 限流维度(global / upload / download / login)

func (*RateLimiter) ProbeAllow

func (rl *RateLimiter) ProbeAllow(ctx context.Context, s scope, probeIP string) bool

ProbeAllow 独立探测(不落内存 limiter 状态,admin /test 实测用)。 probeIP 以 "_test" 后缀隔离,避免污染真实客户端的限流计数。

func (*RateLimiter) SetRedis

func (rl *RateLimiter) SetRedis(rdb *redis.Client)

SetRedis 注入 Redis(use_redis=true 时启用分布式固定窗口)

func (*RateLimiter) SnapshotConfig

func (rl *RateLimiter) SnapshotConfig() RateLimitConfig

SnapshotConfig 返回当前配置副本(并发安全,admin 接口用)

func (*RateLimiter) Stats

func (rl *RateLimiter) Stats() map[string]interface{}

Stats 返回限流器运行状态(/admin/ratelimit/status 用)

func (*RateLimiter) Stop

func (rl *RateLimiter) Stop()

Stop 停止封禁表清扫循环与各 scope 桶托管的自管清理

func (*RateLimiter) UpdateConfig

func (rl *RateLimiter) UpdateConfig(cfg RateLimitConfig)

UpdateConfig 热更新配置

func (*RateLimiter) UploadMiddleware

func (rl *RateLimiter) UploadMiddleware() app.HandlerFunc

UploadMiddleware 上传接口限流

type ScopeError added in v0.32.31

type ScopeError struct {
	Scope string
}

ScopeError 权限不足(Key 有效,但缺该路径所需的能力面)。

与「Key 无效」区分开:前者是 403(Key 是好的、只是能力不够),后者是 401。 调用方据此能区分「换一把 Key」与「重新签发一把范围更宽的 Key」。

func (*ScopeError) Error added in v0.32.31

func (e *ScopeError) Error() string

type SecurityHeadersConfig

type SecurityHeadersConfig struct {
	// EnableHSTS 是否下发 Strict-Transport-Security。三态语义在 conf 层裁决
	// (未配置=生产默认开/开发关),此处只接收最终布尔结果。
	EnableHSTS bool
}

SecurityHeadersConfig 安全响应头中间件的配置项。

  • X-Content-Type-Options: nosniff → 禁止浏览器嗅探 MIME 类型
  • X-Frame-Options: SAMEORIGIN → 防止点击劫持(页面只能被同源 iframe 嵌入)
  • X-XSS-Protection: 1; mode=block → 启用浏览器 XSS 过滤器(旧浏览器)
  • Referrer-Policy: strict-origin-when-cross-origin → 限制 Referrer 泄露
  • Strict-Transport-Security → 仅在生产(HTTPS)下启用 HSTS
  • Cache-Control: no-store → 动态响应禁缓存(防中间盒子/浏览器缓存 泄露会话与私有数据);/assets/ 哈希命名构建产物豁免

另:剥离 Server 指纹头(hertz 在进入 handler 前已写入 "Server: hertz", 框架名帮助攻击者对号入座找现成漏洞利用,删除无副作用)。

可通过 SetSecurityHeadersConfig 调整。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL