Search code examples
phpajaxsecurityphalconcsrf-protection

Using CSRF Protection Service with Ajax in Phalcon


I have trouble with CSRF protection component in Phalcon with Ajax.

html form

<form id="signup-form" onSubmit="onSignUpSubmit(); return false;">
    <input id="username" type="text" placeholder="Username" />
    <input id="password" type="password" placeholder="Password" />
    <input id="email" type="text" placeholder="Email" />
    <input id="signup-csrf-token" name="{{ security.getTokenKey() }}" val="{{ security.getToken() }}" type="hidden" />
    <button class="btn btn-primary btn-block" type="submit">Sign Up</button>
</form> <!-- #signup-form -->

Ajax code

var username    = $('#username', '#signup-form').val(),
    password    = $('#password', '#signup-form').val(),
    email       = $('#email', '#signup-form').val(),
    csrfKey     = $('#signup-csrf-token').attr('name'),
    csrfValue   = $('#signup-csrf-token').attr('val');

    var postData = {
        'username': username,
        'password': password,
        'email': email
    };
    postData[csrfKey] = csrfValue;

    $.ajax({
        type: 'POST',
        url: '{{ url('/accounts/signup.action') }}',
        data: postData,
        dataType: 'JSON',
        success: function(result){
            console.log(result);
        }
    });

When send Ajax request for the first time, the $this->security->checkToken() function in Controller returns true. But for the second time and later, the function returns false.

I think the csrfToken changes for each HTTP request caused this problem. But how to solve it?

Can anyone help me?


Solution

  • You will need to return the new CSRF token in your AJAX success response. Then update your form field with the new token.

    accounts/signup.action

    return json_encode((object) array(
        'output' => $original_output,
        'csrf' => (object) array('name' => $csrf_name, 'value' => $csrf_token)
    ));
    

    Javascript

    $.ajax({
        type: 'POST',
        url: '{{ url('/accounts/signup.action') }}',
        data: postData,
        dataType: 'JSON',
        success: function(result){
            $('input#signup-csrf-token')
                .attr('name', result.csrf.name)
                .val(result.csrf.value);
            console.log(result.output);
        }
    });
    

    You should also change

    $('#signup-csrf-token').attr('val');
    

    to

    $('#signup-csrf-token').val();