Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Grails - Is there a recommended way of dealing with CSRF attacks in AJAX forms?

I am using the Synchronizer Token Pattern for standard forms (useToken = true) but I cannot find any recommended method of dealing with this over AJAX.

EDIT

Since posting this, I have rolled my own solution incorporating Grails existing pattern from above.

In the jQuery ajax I post the entire form (which will include Grails' injected SYNCHRONIZER_TOKEN and SYNCHRONIZER_URI hidden fields) such that the withForm closure can perform as expected in the controller.

The problem is, on successful response, there is no new token set (as the page is not reloaded and the g:form taglib is not evoked) and so I do this manually in the controller calling into the same library as the g:form taglib, and return it in the ajax response, and then reset the hidden field value. See below:

var formData = jQuery("form[name=userform]").serializeArray();

$.ajax({
    type: 'POST',
    url: 'delete',
    data: formData,
    success: function (data) {
        // do stuff
    },
    complete: function (data) {
        // Reset the token on complete
        $("#SYNCHRONIZER_TOKEN").val(data.newToken);
    }
})

in the Controller:

def delete(String selectedCommonName) {

    def messages = [:]
    withForm {
        User user = User.findByName(name)

        if (user) {
            userService.delete(user)
            messages.info = message(code: 'user.deleted.text')

        } else {
            messages.error = message(code: 'user.notdeleted.text')
        }
    }.invalidToken {
            messages.error = message(code: 'no.duplicate.submissions')
    }
    // Set a new token for CSRF protection
    messages.newToken = SynchronizerTokensHolder.store(session).generateToken(params.SYNCHRONIZER_URI)
    render messages as JSON
}

Can anyone identify if I have unknowingly introduced a security flaw in the above solution. It looks adequate to me but I don't like hand rolling anything to do with security.

like image 723
dre Avatar asked May 21 '15 08:05

dre


1 Answers

Nice!

IMO, you'd better reset the token at the same time.

SynchronizerTokensHolder.store(session).resetToken(params.SYNCHRONIZER_URI)

and if you have multiple forms in the same page, define a variable to hold tokens returned from each ajax request.

btw, why not implement the token pattern on your own?

  • Generate a token, e.g., UUID.randomUUID().toString(), and store it into session with the url as the key.
  • Check and reset the token at the satrt of post actions.
like image 75
Yang Avatar answered Sep 28 '22 16:09

Yang