46 lines
1.2 KiB
Go
46 lines
1.2 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// OriginProtection blocks browser-driven cross-site requests to localhost.
|
|
// Same-origin UI requests continue to work; CLI clients without Origin/Referer remain allowed.
|
|
func OriginProtection() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
if isSafeMethod(c.Request.Method) {
|
|
c.Next()
|
|
return
|
|
}
|
|
|
|
if secFetchSite := strings.TrimSpace(c.GetHeader("Sec-Fetch-Site")); secFetchSite == "cross-site" {
|
|
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "cross-site requests are not allowed"})
|
|
return
|
|
}
|
|
|
|
if origin := strings.TrimSpace(c.GetHeader("Origin")); origin != "" && !isLoopbackOrigin(origin) {
|
|
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "origin must be localhost"})
|
|
return
|
|
}
|
|
|
|
if referer := strings.TrimSpace(c.GetHeader("Referer")); referer != "" && !isLoopbackOrigin(referer) {
|
|
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "referer must be localhost"})
|
|
return
|
|
}
|
|
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
func isSafeMethod(method string) bool {
|
|
switch method {
|
|
case http.MethodGet, http.MethodHead, http.MethodOptions:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|