Some checks failed
CI / Lint + Build (push) Has been cancelled
审计修复: - 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流水线
55 lines
1.7 KiB
Go
55 lines
1.7 KiB
Go
package middleware
|
||
|
||
import (
|
||
"crypto/rand"
|
||
"encoding/base64"
|
||
"fmt"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
// generateNonce 生成 16 字节随机 nonce(base64 编码)
|
||
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()
|
||
}
|
||
}
|