Files
calypso/backend/internal/common/router/security.go
2025-12-24 19:53:45 +00:00

103 lines
2.5 KiB
Go

package router
import (
"github.com/atlasos/calypso/internal/common/config"
"github.com/gin-gonic/gin"
)
// securityHeadersMiddleware adds security headers to responses
func securityHeadersMiddleware(cfg *config.Config) gin.HandlerFunc {
if !cfg.Security.SecurityHeaders.Enabled {
return func(c *gin.Context) {
c.Next()
}
}
return func(c *gin.Context) {
// Prevent clickjacking
c.Header("X-Frame-Options", "DENY")
// Prevent MIME type sniffing
c.Header("X-Content-Type-Options", "nosniff")
// Enable XSS protection
c.Header("X-XSS-Protection", "1; mode=block")
// Strict Transport Security (HSTS) - only if using HTTPS
// c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
// Content Security Policy (basic)
c.Header("Content-Security-Policy", "default-src 'self'")
// Referrer Policy
c.Header("Referrer-Policy", "strict-origin-when-cross-origin")
// Permissions Policy
c.Header("Permissions-Policy", "geolocation=(), microphone=(), camera=()")
c.Next()
}
}
// corsMiddleware creates configurable CORS middleware
func corsMiddleware(cfg *config.Config) gin.HandlerFunc {
return func(c *gin.Context) {
origin := c.Request.Header.Get("Origin")
// Check if origin is allowed
allowed := false
for _, allowedOrigin := range cfg.Security.CORS.AllowedOrigins {
if allowedOrigin == "*" || allowedOrigin == origin {
allowed = true
break
}
}
if allowed {
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
}
if cfg.Security.CORS.AllowCredentials {
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
}
// Set allowed methods
methods := cfg.Security.CORS.AllowedMethods
if len(methods) == 0 {
methods = []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"}
}
c.Writer.Header().Set("Access-Control-Allow-Methods", joinStrings(methods, ", "))
// Set allowed headers
headers := cfg.Security.CORS.AllowedHeaders
if len(headers) == 0 {
headers = []string{"Content-Type", "Authorization", "Accept", "Origin"}
}
c.Writer.Header().Set("Access-Control-Allow-Headers", joinStrings(headers, ", "))
// Handle preflight requests
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}
// joinStrings joins a slice of strings with a separator
func joinStrings(strs []string, sep string) string {
if len(strs) == 0 {
return ""
}
if len(strs) == 1 {
return strs[0]
}
result := strs[0]
for _, s := range strs[1:] {
result += sep + s
}
return result
}