Files
mce/internal/middleware/security.go
Victor_Jay 2449a27eb5
Some checks failed
CI / Lint + Build (push) Has been cancelled
refactor: CSP nonce替代unsafe-inline, HSTS始终启用, router/api拆分, 管理后台+用户设置页拆分, DB健康降级, 时区配置, UID统一解析, CI接入
审计修复:
- CSP: unsafe-inline移除, nonce替代; unsafe-eval保留供Vditor使用
- HSTS: 始终启用(不再依赖release模式)
- Controller Exp: common.GetGinExp包装, 不再直接读context
- UID解析: common.ParseUIDParam统一, follow/admin controller改用

重构:
- router/api.go(198行)拆为7个域文件: auth/settings/posts/comments/reactions/social/studio
- 管理后台站点设置: 单页拆为4个子页(brand/security/registration/content)+子菜单
- 前台用户设置页: 1201行拆为6个独立模板+6个独立JS文件

新增:
- DB健康检测中间件(middleware/db_health.go): 5s ping+降级页面
- 时区配置: config.yaml server.timezone→time.Local初始化
- .gitea/workflows/ci.yml: strict模式CI流水线
2026-06-22 03:06:24 +08:00

55 lines
1.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package middleware
import (
"crypto/rand"
"encoding/base64"
"fmt"
"github.com/gin-gonic/gin"
)
// generateNonce 生成 16 字节随机 noncebase64 编码)
func generateNonce() string {
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
// 降级:使用固定长度回退(极度罕见,仅 /dev/urandom 故障时)
panic("failed to generate CSP nonce: " + err.Error())
}
return base64.StdEncoding.EncodeToString(b)
}
// SecurityHeaders 添加安全相关 HTTP 响应头
// 作为纵深防御,不影响业务逻辑,纯附加
func SecurityHeaders() gin.HandlerFunc {
return func(c *gin.Context) {
// 生成一次性 nonce用于 CSP 策略和模板内 <script>/<style> 标签
nonce := generateNonce()
c.Set("csp_nonce", nonce)
// Content-Security-Policy
// script-src: 本站 + nonce替代 unsafe-inline保留 unsafe-eval 供 Vditor 使用
// style-src: 本站(所有内联样式已使用 nonce
// img-src: 本站 + data: URI头像裁切+ blob:(粘贴图片)
c.Header("Content-Security-Policy",
"default-src 'self'; "+
fmt.Sprintf("script-src 'self' 'unsafe-eval' 'nonce-%s'; ", nonce)+
"style-src 'self' 'nonce-"+nonce+"'; "+
"img-src 'self' data: blob:; "+
"connect-src 'self'")
// 禁止 MIME 类型嗅探
c.Header("X-Content-Type-Options", "nosniff")
// 禁止被 frame 嵌入(防点击劫持)
c.Header("X-Frame-Options", "DENY")
// 引用策略
c.Header("Referrer-Policy", "strict-origin-when-cross-origin")
// HSTS始终启用max-age=1年含子域名允许 preload 列表收录)
c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")
c.Next()
}
}