Help us shape the future of Go! Take the 2026 Go Developer Survey

middleware

package
v0.32.41 Latest Latest
Warning

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

Go to latest
Published: Oct 31, 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