justinas / nosurf

CSRF protection middleware for Go.
http://godoc.org/github.com/justinas/nosurf
MIT License
1.59k stars 126 forks source link

How to use with gin? #30

Closed dre1080 closed 7 years ago

dre1080 commented 9 years ago

Any way to use this with the gin framework? See issue here

elithrar commented 9 years ago

https://godoc.org/github.com/gin-gonic/gin#WrapH should do the trick - you want to call gin.WrapH(nosurf.NewPure()).

docmerlin commented 9 years ago

NewPure expects an argument.

justinas commented 7 years ago

Closing after inactivity.

hazcod commented 5 years ago

I am having the same issue. Any ideas?

func NewPure(handler http.Handler) http.Handler {
CyganFx commented 3 years ago

Doesn't work with gin framework

justinas commented 3 years ago

@CyganFx please open a new issue with the code you're running.

sam0737 commented 3 months ago

I know it's 2024 but...nosurf seems to be the only library doesn't require session but cookie only.

func YourRouteInstaller(r gin.IRouter) {
    // r could be gin.Engine
    g := r.Group("/api", CsrfMiddleware(true))
    // code continues...
}

func CsrfMiddleware(secure bool) gin.HandlerFunc {
    return WrapHH(func(h http.Handler) http.Handler {
        n := nosurf.New(h)
        n.SetBaseCookie(http.Cookie{Name: "csrf_token", HttpOnly: true, Secure: secure})
        return n
    })
}

type wrappedResponseWriter struct {
    gin.ResponseWriter
    writer http.ResponseWriter
}

func (w *wrappedResponseWriter) Write(data []byte) (int, error) {
    return w.writer.Write(data)
}

func (w *wrappedResponseWriter) WriteString(s string) (n int, err error) {
    return w.writer.Write([]byte(s))
}

type NextRequestHandler struct {
    c *gin.Context
}

// Run the next request in the middleware chain and return
// See: https://godoc.org/github.com/gin-gonic/gin#Context.Next
func (h *NextRequestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    h.c.Writer = &wrappedResponseWriter{h.c.Writer, w}
    h.c.Request = r
    h.c.Next()
}

// Wrap something that accepts an http.Handler, returns an http.Handler
func WrapHH(hh func(h http.Handler) http.Handler) gin.HandlerFunc {
    // Steps:
    // - create an http handler to pass `hh`
    // - call `hh` with the http handler, which returns a function
    // - call the ServeHTTP method of the resulting function to run the rest of the middleware chain

    return func(c *gin.Context) {
        hh(&NextRequestHandler{c}).ServeHTTP(c.Writer, c.Request)
        if c.Writer.Written() {
            // If the response has been written, abort the rest of the middleware chain
            c.Abort()
        }
    }
}

By the way, https://github.com/turtlemonvh/gin-wraphh has two major problem

  1. it does not do c.Abort(). Even if token failed to validate, the handler chain is not stopped which defeat the purpose.
  2. missing h.c.Request = r. nosurf replaced the request with key embedded in context value, and such the original request must be replaced. Otherwise there is no way to get the token out in the handler and place it in HTML tag or alike.