nyanimedb/modules/backend/middlewares/csrf.go

70 lines
1.6 KiB
Go

package middleware
import (
"crypto/subtle"
"net/http"
"github.com/gin-gonic/gin"
)
// CSRFMiddleware для Gin
func CSRFMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// Пропускаем безопасные методы
if !isStateChangingMethod(c.Request.Method) {
c.Next()
return
}
// 1. Получаем токен из заголовка
headerToken := c.GetHeader("X-XSRF-TOKEN")
if headerToken == "" {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"error": "missing X-XSRF-TOKEN header",
})
return
}
// 2. Получаем токен из cookie
cookie, err := c.Cookie("xsrf_token")
if err != nil {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"error": "missing xsrf_token cookie",
})
return
}
// 3. Безопасное сравнение
if subtle.ConstantTimeCompare([]byte(headerToken), []byte(cookie)) != 1 {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"error": "CSRF token mismatch",
})
return
}
// 4. Опционально: сохраняем токен в контексте
c.Set("csrf_token", headerToken)
c.Next()
}
}
func isStateChangingMethod(method string) bool {
switch method {
case http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete:
return true
default:
return false
}
}
// CSRFTokenFromGin извлекает токен из Gin context
func CSRFTokenFromGin(c *gin.Context) (string, bool) {
token, exists := c.Get("xsrf_token")
if !exists {
return "", false
}
if s, ok := token.(string); ok {
return s, true
}
return "", false
}