Search code examples
gocsrfcsrf-token

Golang - POST failed (NoSurf CSRF)


I am using NoSurf for CSRF in my Golang code. This is from Trevor Sawler's Golang course, section 8.2. If anything, I decide to go with Bootstrap 5 instead the tutorial's Bootstrap 4. My code can be found here. To activate the web server, I use go run ./cmd/web/*.go. So inside cmd\web\routes.go, the following are available for the localhost:8080/search-availability page:

mux.Get("/search-availability", handlers.Repo.Availability)
mux.Post("/search-availability", handlers.Repo.AvailabilityPost)

Inside pkg\handlers\handlers.go, I have:

func (m *Repository) Availability(w http.ResponseWriter, r *http.Request) {
    log.Println("Availability")
    render.RenderTemplate(w, r, "search-availability.page.tmpl", &models.TemplateData{})
}

func (m *Repository) AvailabilityPost(w http.ResponseWriter, r *http.Request) {
    log.Println("Before Write")
    w.Write([]byte("Posted to search-availability"))
    log.Println("After Write")
}

Inside pkg\render\render.go, I have:

func AddDefaultData(td *models.TemplateData, r *http.Request) *models.TemplateData {
    td.CsrfToken = nosurf.Token(r)
    return td
}

Inside pkg\models\templatedata.go, I have:

type TemplateData struct {
    StringMap map[string]string
    IntMap    map[string]int
    FloatMap  map[string]float64
    Data      map[string]interface{}
    CsrfToken string
    Flash     string
    Error     string
}

Inside templates\search-availability.page.tmpl, I have on line 13:

<input type="text" name="csrf_token" value="{{.CsrfToken}}">

After choosing the start and end date from the localhost:8080/search-availability page, clicking on the Search Availability button always gets me a 400 Bad Request as seen below.

img

From the terminal, I can see that the code never enters AvailabilityPost:

2023/03/12 19:06:48 Hit the page
2023/03/12 19:06:48 Availability
2023/03/12 19:06:51 Hit the page

However if I disable nosurf inside cmd\web\routes.go like this:

func routes(app *config.AppConfig) http.Handler {
    mux := chi.NewRouter()
    mux.Use(middleware.Recoverer)
    mux.Use(WriteToConsole)
    // mux.Use(NoSurf)
    mux.Use(SessionLoad)
    ...
    mux.Get("/search-availability", handlers.Repo.Availability)
    mux.Post("/search-availability", handlers.Repo.AvailabilityPost)
    ...

    fileServer := http.FileServer(http.Dir("./static/"))
    mux.Handle("/static/*", http.StripPrefix("/static", fileServer))
    return mux
}

The browser shows a successful post:

Posted to search-availability

The terminal also confirms:

2023/03/12 19:19:46 Hit the page
2023/03/12 19:19:46 Availability
2023/03/12 19:19:48 Hit the page
2023/03/12 19:19:48 Before Write
2023/03/12 19:19:48 After Write

Could someone point out what I have done wrong? I am not getting a successful post as seen in the tutorial. The material is a bit dated, but I can't see how it be an issue.


Solution

  • You didn't send the CSRF token input on form submit. To fix this, you can either add the form attribute to your CSRF input element and set it to the ID of your form (date-picker), or move the CSRF input element inside the form element.