AJAX and CSRF in Spring

3.9k Views Asked by At

I'm sending a CSRF token in an HTTP request header and in a POST hidden field to guard against CSRF attacks something like the following.

var request;
var timeout;

function insert(callback)
{
    if(!request) 
    {
        CKEDITOR.instances.txtAboutProducts.updateElement();
        var contents=$("#txtAboutProducts").val();
        var csrf_token=$("#token").val();

        if(contents==null||contents=='')
        {
            alert("Please enter the contents.");
            return;
        }

        request = $.ajax({
            headers: { 
                    'Accept': 'application/json',
                    'Content-Type': 'application/json' 
                },
            datatype:"json",
            type: "POST",
            url: "../admin_side/AboutProducts.htm",
            data: JSON.stringify({"contents":contents, "token":csrf_token}),

            beforeSend: function (xhr)
            {
                xhr.setRequestHeader('X-CSRF-Token', csrf_token); 
            },
            success: function(response)
            {
                callback(response);
            },
            complete: function()
            {
                timeout = request = null;
            },
            error: function(request, status, error)
            {
                if(status!=="timeout"&&status!=="abort") // or just if(status==="error")
                {
                    alert(status+" : "+error);
                }
                callback(request);
            }
        });
        timeout = setTimeout(function() {
            if(request) 
            {
                request.abort();
                alert("The request has been timed out.");
            }
      }, 300000); //5 minutes.
    }
}

This function is meant to insert CMS contents held by a CKEditor to be inserted into the database with a POST AJAX call to Spring via JSON (when a button is clicked).

The conditional check if(!request){...} in the beginning of the function body is just to prevent duplicate AJAX calls (probably by impatient users).

The randomly generated token value is stored in a hidden field as soon as the page is loaded and retrieved in a JavaScript variable.

var csrf_token=$("#token").val();

The following handler then sets a header which is to be sent along with the request.

beforeSend: function (xhr)
{
    xhr.setRequestHeader('X-CSRF-Token', csrf_token); 
},

Why is a header required? Is a token sent as a hidden field not sufficient alone? Is it necessary to check the header because of old Flash Players (just as an example)?


With this approach, on the server side, I'm checking, if the values of both the header and the hidden field match (along with checking the existence of that token in the session {checking the existence of the token in the session is nowhere mentioned though}).

The method in the Spring controller class which is invoked by the above mentioned JavaScript function is as follows.

@RequestMapping(value=("admin_side/AboutProducts"), method=RequestMethod.POST)
private @ResponseBody CKEditorContentsHandler insert(@RequestBody final CKEditorContentsHandler object, final HttpServletResponse response, final HttpServletRequest request)
{
    if(object!=null&&StringUtils.isNotBlank(object.getToken())&&StringUtils.isNotBlank(request.getHeader("X-CSRF-Token"))&&sessionTokenService.isTokenValid(object.getToken())&&object.getToken().equals(request.getHeader("X-CSRF-Token")))
    {
        aboutProductsService.insert(object.getContents());
        object.setMessage("Insertion done successfully.");
        object.setStatus(1);
    }
    else
    {
        object.setMessage("The authentication token cannot be verified.");
        object.setStatus(-1);
    }
    return object;
}

Where CKEditorContentsHandler , the first parameter of this method is a simple Java class holding just a few properties to fulfill the needs.


It is also recommended to store the token in a cookie and send it along with the posted data (the hidden field) and check, if the posted data and the cookie values match. If they don't, then it is a possible CSRF heck (because the attackers cannot read or modify cookies on the victim's browser because of the same origin policy).

Afterwards, why is a header required? What actually is the recommended way to guard against (or at least mitigate) CSRF attacks?


The following answer summarizes it well.

anti-CSRF token and Javascript

0

There are 0 best solutions below