feat: csrf tokens handling
This commit is contained in:
parent
ab29c33f5b
commit
6995ce58f6
12 changed files with 233 additions and 5 deletions
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
oapi "nyanimedb/api"
|
||||
handlers "nyanimedb/modules/backend/handlers"
|
||||
middleware "nyanimedb/modules/backend/middlewares"
|
||||
"nyanimedb/modules/backend/rmq"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
|
|
@ -45,6 +46,8 @@ func main() {
|
|||
|
||||
r := gin.Default()
|
||||
|
||||
r.Use(middleware.CSRFMiddleware())
|
||||
// jwt middle will be here
|
||||
queries := sqlc.New(pool)
|
||||
|
||||
// === RabbitMQ setup ===
|
||||
|
|
@ -63,7 +66,6 @@ func main() {
|
|||
rpcClient := rmq.NewRPCClient(rmqConn, 30*time.Second)
|
||||
|
||||
server := handlers.NewServer(queries, publisher, rpcClient)
|
||||
// r.LoadHTMLGlob("templates/*")
|
||||
|
||||
r.Use(cors.New(cors.Config{
|
||||
AllowOrigins: []string{"*"}, // allow all origins, change to specific domains in production
|
||||
|
|
|
|||
70
modules/backend/middlewares/csrf.go
Normal file
70
modules/backend/middlewares/csrf.go
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue