feat(csp): auto-inject purchase_subscription_url origin into frame-src

This commit is contained in:
erio
2026-03-02 00:19:25 +08:00
parent c3ac68af2a
commit 8a82a2a648
5 changed files with 72 additions and 20 deletions

View File

@@ -41,7 +41,9 @@ func GetNonceFromContext(c *gin.Context) string {
}
// SecurityHeaders sets baseline security headers for all responses.
func SecurityHeaders(cfg config.CSPConfig) gin.HandlerFunc {
// getFrameSrc is an optional function that returns an extra origin to inject into frame-src;
// pass nil to disable dynamic frame-src injection.
func SecurityHeaders(cfg config.CSPConfig, getFrameSrc func() string) gin.HandlerFunc {
policy := strings.TrimSpace(cfg.Policy)
if policy == "" {
policy = config.DefaultCSPPolicy
@@ -51,6 +53,13 @@ func SecurityHeaders(cfg config.CSPConfig) gin.HandlerFunc {
policy = enhanceCSPPolicy(policy)
return func(c *gin.Context) {
finalPolicy := policy
if getFrameSrc != nil {
if origin := getFrameSrc(); origin != "" {
finalPolicy = addToDirective(finalPolicy, "frame-src", origin)
}
}
c.Header("X-Content-Type-Options", "nosniff")
c.Header("X-Frame-Options", "DENY")
c.Header("Referrer-Policy", "strict-origin-when-cross-origin")
@@ -61,12 +70,10 @@ func SecurityHeaders(cfg config.CSPConfig) gin.HandlerFunc {
if err != nil {
// crypto/rand 失败时降级为无 nonce 的 CSP 策略
log.Printf("[SecurityHeaders] %v — 降级为无 nonce 的 CSP", err)
finalPolicy := strings.ReplaceAll(policy, NonceTemplate, "'unsafe-inline'")
c.Header("Content-Security-Policy", finalPolicy)
c.Header("Content-Security-Policy", strings.ReplaceAll(finalPolicy, NonceTemplate, "'unsafe-inline'"))
} else {
c.Set(CSPNonceKey, nonce)
finalPolicy := strings.ReplaceAll(policy, NonceTemplate, "'nonce-"+nonce+"'")
c.Header("Content-Security-Policy", finalPolicy)
c.Header("Content-Security-Policy", strings.ReplaceAll(finalPolicy, NonceTemplate, "'nonce-"+nonce+"'"))
}
}
c.Next()