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?
Cross-Site Request Forgery (CSRF) is an attack where a malicious site sends a request to a vulnerable site where the user is currently logged in. Here is an example of a CSRF attack: A user logs into www.example.com using forms authentication. The server authenticates the user.
Cross site request forgery (CSRF), also known as XSRF, Sea Surf or Session Riding, is an attack vector that tricks a web browser into executing an unwanted action in an application to which a user is logged in. A successful CSRF attack can be devastating for both the business and user.
In a successful CSRF attack, the attacker causes the victim user to carry out an action unintentionally. For example, this might be to change the email address on their account, to change their password, or to make a funds transfer.
Placing session and/or CSRF tokens in the URL increases the vulnerability to attacks. The secure way of using CSRF validation is to use it only for POST requests (or equivalent request like PUT/DEL/UPDATE and so on.)
I've created a CSRF protection package for Go called nosurf. Here's how it handles the areas you mentioned:
nosurf
takes care of cancelling the request and either returning 403 or calling your custom failure handler (if set). You don't have to have if CsrfCheckOk(r) { ... }
or anything like that in your code.So that's it, even though I'm not sure it is the best way to handle CSRF all-around. A package for a specific framework might handle it better in some ways due to tight integration.
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)
It is a good idea to regenerate both the token and session ID often. Given a persistent attacker and a viable entry vector, it's just a matter of time until the attacker obtains both. If, however, at least one of both identifiers regenerates before the attacker is able to crack the current one, then no problem.
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?
You can update cookies and CSRF tokens through AJAX if you want to give your client vast time to fill out a form.
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?
The more tightly bound a token is to a certain action that requires authentication, the more fine-grained control you have. If you can uniquely identify each form in your application then I'd say do it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With