Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSRF in Go Web Applications

Tags:

security

go

csrf

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?

like image 295
elithrar Avatar asked Oct 02 '13 04:10

elithrar


People also ask

What is CSRF in web API?

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.

Is CSRF a web server attack?

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.

What is CSRF attack with example?

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.

Can we send CSRF token in URL?

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.)


2 Answers

I've created a CSRF protection package for Go called nosurf. Here's how it handles the areas you mentioned:

  • Token is created by taking bytes from CS PRNG and encoding them using base64. It is not regenerated for every page load or every form, though there is a user-callable function for regenerating the token.
  • It is then stored in a cookie (not a session, as it's a generic middleware not intended for any specific framework only). The cookie lasts one year, but you can easily modify this duration.
  • 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.
  • Sadly, it doesn't address token expiring inbetween the page load and the form submission.

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.

like image 21
justinas Avatar answered Oct 09 '22 06:10

justinas


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.

like image 188
thwd Avatar answered Oct 09 '22 08:10

thwd