POST via Ajax returns 403 with CSRF enabled |
Hello Community,
I've deployed the latest Codeigniter 4.0.2 version and I have an issue when submitting a form with Post method via Ajax when CSRF is enabled, I've tried my best to figure out what is wrong but still no luck. Notice that: - When CSRF is disabled, the Ajax call is successful - If I don't use Ajax and I enable CSRF, the controller handles correctly the request with 200 code Allow me to share with you the details: - CSRF enabled globally in the file App\Config\Filters.php: Code: // Always applied before every request - CSRF configuration: Code: public $CSRFTokenName = 'csrf_token'; - A simple contact form with the CSRF hidden input created manually: Code: <form name='contactForm' class="contactForm" id='contactForm'> - When submitting the form, the following Javascript code is being executed: Code: const apiUrl = '/api/contact'; - When accessing URL /api/contact, the following Controller handles the request: Code: <?php namespace App\Controllers; - Analyzing the Payload, it is correct: Code: csrf_token=563def7f2e22a4df1f6e53ce8f0b75d7&msg_name=jdoe It matches with the cookie: Code: Cookie: csrf_cookie=563def7f2e22a4df1f6e53ce8f0b75d7 Mind that I've tried another way to pass the CSRF token, directly to the headers: Code: $.ajaxSetup({ It is added correctly: Code: csrf_token: 563def7f2e22a4df1f6e53ce8f0b75d7 - In all my attempts, the same result, 403 error code: Code: code: 403 What am I missing here? CSRF token name and CSRF hash are passed correctly to the controller but it keeps showing 403 error only when the request is performed via Ajax. Could you please shed some light on this issue? Thank you in advance for taking your time reading this.
When using csrf, a unique token is automatically regenerated for each page update (for HTML), but not for AJAX (as the page isn’t updated)... having a brief glance at your code, it does look like you’re passing the token correctly and also for every reply to the server, which may point to your reply being rejected AFTER the first use of the token (do you perhaps find you get one AJAX submission in, then it goes pear-shaped from the second one (?)).
From a comment in your code, you seem aware of one way around this being to set $CSRFRegenerate=FALSE in App.php, which will keep a single csrf token valid for the whole browser session (likely not quite as secure as having a new one generated each time... though it is an easy fix for the AJAX submission problem, and also when using the browser's navigate back button)… so as an initial suggestion, I’d suggest changing and leaving $CSRFRegenerate set to FALSE, which would make it easier to get working initially (which then you could change later, if required). As an aside, having been burnt a few times with trailing spaces in names and values, and although it probably is of no consequence here, as I'm paranoid, I’d normally encode: Code: name="<?= csrf_token() ?>" value="<?= csrf_hash() ?>" id="msg_csrf"/> Code: name="<?=csrf_token();?>" value="<?=csrf_hash();?>" id="msg_csrf"/> <!-- semicolon added to be pedantically correct -->
(04-06-2020, 08:27 AM)Gary Wrote: When using csrf, a unique token is automatically regenerated for each page update (for HTML), but not for AJAX (as the page isn’t updated)... having a brief glance at your code, it does look like you’re passing the token correctly and also for every reply to the server, which may point to your reply being rejected AFTER the first use of the token (do you perhaps find you get one AJAX submission in, then it goes pear-shaped from the second one (?)). Thank you Gary, Note that: - There is only one submission - Yep, I'm aware of CSRFRegenerate, but the issue persists while setting it to false as well - The semicolon didn't make a difference Any other ideas? Might it be a bug? ![]()
Once you've checked the setting CSRFRegenerate is FALSE, it may be worth trying setting dataType: 'html' in your AJAX routine (?).
You could also try removing contentType: "application/json" to see whether that makes a difference.
(04-06-2020, 12:08 PM)Gary Wrote: Once you've checked the setting CSRFRegenerate is FALSE, it may be worth trying setting dataType: 'html' in your AJAX routine (?). Hi Gary, The contentType was causing this malfunction... IMO it doesn't make any sense but at least now it's working, here the working Ajax function: Code: const sendContactForm = (contactFormData) => { Now that it works, could you please clarify if this is expected? Why the contentType was causing this? It should be included, don't you think? Thanks
As far as I'm aware, contentType is a page header field.
I suspect when it arrives in your AJAX submission, it confuses things on the server side because it's not something that's expected. I don't believe it's supposed to be included. data: is what is submitted to the server, and although there's two or three formats this data: can be, it is ultimately always converted to a query string before it is sent (so the server is only ever expecting this and not a programmer-spec'd contentType).
(04-06-2020, 03:16 PM)Gary Wrote: As far as I'm aware, contentType is a page header field. Yes, CI expects urlencoded, I would swear I did try x-www-form-urlencoded in one of my previous attempts... anyway, here it is the valid Ajax submission, it might help someone in the future: Code: const sendContactForm = (contactFormData) => { Thanks you! ![]() |
Welcome Guest, Not a member yet? Register Sign In |