Welcome Guest, Not a member yet? Register   Sign In
Correct way to re-generate CSRF for sequential AJAX calls
#1
Question 

I have a vague memory of seeing this being discussed in another thread, but I can't find it now.
Scenario:
  • Controller uses Model to fetch data and pass it onto View
  • View contains functionality to refresh data via JS/AJAX call
  • The first call succeeds, CSRF checks pass
  • The second call fails, CSRF checks do not pass
I understand why. The AJAX call goes through CI and the CSRF token is re-generated. Since the initial View is never re-loaded, the form field contains the previous CSRF token.

Now (at last) to my question:

What is best practice to "inform" the initial View of the new CSRF token? I was thinking about simply including it in the response from the AJAX call to the JS code in the View, and then letting the JS code update the form's CSRF field with the new value.

Can anyone see any security issues with this?

The first call won't succeed if the token is stale or invalid, so there won't be a new one generated. The refreshed CSRF token will only be passed back on a successful call.

-joho
Reply
#2

I'm not sure there is anything to worry about security with this approach. It is still the same as when the form is submitted directly (without AJAX). When the form is returned back to the user, the CSRF token will be regenerated and updated in the form, for subsequent submissions.
Reply
#3

(08-24-2023, 05:25 AM)sammyskills Wrote: I'm not sure there is anything to worry about security with this approach. It is still the same as when the form is submitted directly (without AJAX). When the form is returned back to the user, the CSRF token will be regenerated and updated in the form, for subsequent submissions.

That was my thinking, but I was worried I had overlooked something, being a CI noob Smile

-joho
Reply
#4

How to Send AJAX request with CSRF token in CodeIgniter 4
What did you Try? What did you Get? What did you Expect?

Joined CodeIgniter Community 2009.  ( Skype: insitfx )
Reply
#5

(08-24-2023, 11:04 PM)InsiteFX Wrote: How to Send AJAX request with CSRF token in CodeIgniter 4

Indeed. (Though I prefer pure JS.) It's a good article nevertheless.

-joho
Reply
#6

For me i do something like this
#HTML
Code:
<!--First Load CSRF Token from PHP-->
<input type="hidden" name="<?= csrf_token() ?>" value="<?= csrf_hash() ?>" id="csrfcode">
<!--You can put the csrf field inside the form, for me i use this field for whole page purpose-->
<!--End of csrf-->
<form method="post" id="dataEmail">
    <label for="email">E-mail</label>
    <input type="email" name="email" id="email" placeholder="[email protected]">
    <button type="submit">Save E-mail</button>
</form>

#JS
Code:
var formEmail = document.getElementById('dataEmail');
    formuser.addEventListener("submit", (e) => {
        let csrfField = document.getElementById('csrfcode')
        e.preventDefault();
        let formData = new FormData(forEmail);
        formData.append(csrfField.name, csrfField.value)
        window.fetch(baseUrl + '/email', {
            method: 'POST',
            headers: {
                "X-Requested-With": 'XMLHTTPRequest'
            },
            body: formData,
        }).then(function (response) {
            return response.json();
        }).then(function (data) {
            if (data.status) {
                //Do something if true
                console.log(data)
            }
            if (data?.csrf) {
                //Set csrfField to new token
                csrfField.value = data.csrf
            }
        }).catch(function (err) {
            // There was an error
            console.warn('There was an error', err);
        })

#Controller
PHP Code:
public function changeEmailUser()
    {
        if ($this->request->isAJAX()) {
            if ($this->request->is('post')) {
                $data = array();
                $data['csrf'] = csrf_hash(); //Renew the csrf token for next request
                $data['status'] = false;
                $email $this->request->getPost('email');
                $postData = [
                    'email' => [
                        'rules' => 'required|is_unique[auth_identities.secret]|valid_email',
                        'errors' => [
                            'required' => 'E-mail is required',
                            'is_unique' => 'E-mail already taken',
                            'valid_email' => 'E-mail not valid',
                        ]
                    ]
                ];
                if ($this->validate($postData)) {
                    if ($this->usersModel->changeEmail($id$email)) {
                        $data['status'] = true;
                    }
                } else {
                    $error $this->validator->getErrors();
                    $errorItem = array();
                    if (array_key_exists('email'$error)) {
                        array_push($errorItem$error['email']);
                    }
                    $data = [
                        'errors' => $errorItem,
                    ];
                }
                return $this->response->setJSON($data);
            }
        }
        return $this->response->setStatusCode(404'Nope. Not here.');
    
Reply




Theme © iAndrew 2016 - Forum software by © MyBB