I want to implement CSRF prevention in my Go web application. Users don't log in, but they do fill out forms and pay (via Stripe Checkout).
Posting something sets a key in a session variable (cookie) so they can later edit what they've posted, and a URL in an email allows them to come back when the cookie has expired and edit it again if need be.
From what I can see, I can use https://code.google.com/p/xsrftoken/ with the "double submitted cookie" method to implement CSRF prevention by:
Generate a CSRF token against an arbitrary user ID (uuid.V4() via go-uuid), like so:
if session.Values["id"] == "" { session.Values["id"] = uuid.NewV4() } csrfToken := xsrftoken.Generate(csrfKey, session.Values["id"], "/listing/new/post")
... and store that in the session and render it in a hidden field in the template:
session.Values["csrfToken"] = csrfToken ... <input type="hidden" id="_csrf" value={{ .csrfToken }}>
When the user submits the form, I need to get the ID I generated, confirm that the submitted
csrfToken
from the form matches the one in the session, and if so, validate it with the xsrf package to confirm it hasn't expired:userID := session.Values["id"] if session.Values["csrfToken"] != r.PostFormValue("csrfToken") { http.Redirect(w, r, "/listing/new", 400) } if !xsrftoken.Valid(session.Values["csrfToken"], csrfKey, userID, "/listing/new/post") { http.Redirect(w, r, "/listing/new", 400) }
My pertinent questions are:
Should I generate a new token every time the form is rendered? Or is it acceptable to re-use a non-expired token for a single user session? Update: According to this answer I should only generate a new token per session (i.e. so the same user gets the same token on the same form, until the token expires)
Given the updated question, how do I handle the situation where a created token expires between the time the user requests the form and then submits the form? (perhaps it had 10 minutes left, and they alt+tabbed out for a while) Re-direct them back to the form (re-populated, of course!) and generate a new session id + csrf token?
Is there a different way to do this? Coding Horror indicates that SO generates a unique key for every HTML form sent to the client? How would I go about going down this route with the xsrf package, given that it wants a userID when generating a new key?
What else have I overlooked?