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 }