cms icon indicating copy to clipboard operation
cms copied to clipboard

CSRF token mismatch full cache strategy

Open danielreales7 opened this issue 1 year ago • 15 comments

Bug description

If I don't use the full cache strategy on my site, the forms work fine. The problem comes when they are used. If I try to send the form, I get the following: CSRF token mismatch.

It seems that the token is not being updated behind the scenes.

I tried wrapping the form with a {{ nocache }} and it was fixed.

Then I realized that if I send the form in Spanish without filling in the inputs, I get the error messages in Spanish. So far so good. If I go to the English site and send it, it shows me the messages in English. Also good. But if I go back to Spanish, it shows me the English messages.

According to the documentation, if full cache is used, we must use {{ nocache }} https://statamic.dev/static-caching#csrf-tokens

Same here: https://statamic.dev/forms#caching

How to reproduce

Enabling the full cache strategy and multisite.

Logs

No response

Environment

Laravel Version: 11.16.0
PHP Version: 8.2.20
Composer Version: 2.7.9
Environment: production
Debug Mode: OFF
Maintenance Mode: OFF
Timezone: UTC
Locale: es

Cache
Config: CACHED
Events: NOT CACHED
Routes: CACHED
Views: CACHED

Drivers
Broadcasting: log
Cache: file
Database: sqlite
Logs: stack / single
Mail: smtp
Queue: sync
Session: file

Statamic
Addons: 5
Sites: 2
Stache Watcher: Disabled (auto)
Static Caching: full
Version: 5.16.0 PRO

Statamic Addons
spatie/statamic-responsive-images: 5.0.0
studio1902/statamic-peak-browser-appearance: 3.5.0
studio1902/statamic-peak-commands: 8.4.0
studio1902/statamic-peak-seo: 8.15.3
studio1902/statamic-peak-tools: 6.3.0

Installation

Starter Kit using via CLI

Additional details

No response

danielreales7 avatar Sep 14 '24 09:09 danielreales7

The problem also occurs when doing the opposite with the application languages. If I access English first and then Spanish and go back to English, it returns the messages in Spanish.

It depends on the first page I cache. I've been debugging and it comes from this function in src/Http/Requests/FrontendFormRequest.php:

public function validateResolved()
    {
        // If this was submitted from a front-end form, we want to use the appropriate language
        // for the translation messages. If there's no previous url, it was likely submitted
        // directly in a headless format. In that case, we'll just use the default lang.
        $site = ($previousUrl = session()->previousUrl()) ? Site::findByUrl($previousUrl) : null;

        return $this->withLocale($site?->lang(), fn () => parent::validateResolved());
    }

danielreales7 avatar Sep 15 '24 02:09 danielreales7

You shouldn't need to wrap the form in the {{ nocache }} tag. CSRF tokens should be automatically replaced when using full-measure caching.

Are you using the native {{ form }} tag? Are there any console errors when viewing a cached page?

duncanmcclean avatar Sep 16 '24 08:09 duncanmcclean

You shouldn't need to wrap the form in the {{ nocache }} tag. CSRF tokens should be automatically replaced when using full-measure caching.

Are you using the native {{ form }} tag? Are there any console errors when viewing a cached page?

Now I was testing locally and I am not getting the CSRF mismatch error. I was watching production. I'll see if the token expires and try again to show you the error.

But it keeps happening that when a page is cached, whether it is English or Spanish, and I submit a form, I get error messages for the wrong language.

danielreales7 avatar Sep 16 '24 09:09 danielreales7

But it keeps happening that when a page is cached, whether it is English or Spanish, and I submit a form, I get error messages for the wrong language.

That's with the {{ nocache }} removed, yeah?

How are you submitting the forms? With an AJAX request or just a normal <form> submit?

duncanmcclean avatar Sep 16 '24 09:09 duncanmcclean

But it keeps happening that when a page is cached, whether it is English or Spanish, and I submit a form, I get error messages for the wrong language.

That's with the {{ nocache }} removed, yeah?

Yes, exactly.

If I access the English form without having any page cached, I get the error messages in English. If I go to Spanish and submit the form, I get the error messages in Spanish. Now once both are cached, if I go back to the English form, I get the Spanish messages. The same thing happens in a different order.

danielreales7 avatar Sep 16 '24 09:09 danielreales7

Look now:

image

I have deleted the browser cookies. Now both pages are cached. If I try to submit the form again I get what I told you above.

This happens both with and without the {{ nocache }} tag.

danielreales7 avatar Sep 16 '24 09:09 danielreales7

How are you submitting the form? Are you using AJAX?

duncanmcclean avatar Sep 16 '24 10:09 duncanmcclean

I'm using precognition. It's the peak studio component but I've extracted the form submission to a dedicated js file.

This is my sending.js:

 export default () => ({
    success: false,
    submitted: false,
    form: null,
    init() {
        this.form = this.$form(
            'post',
            this.$refs.form.getAttribute('action'),
            JSON.parse(this.$refs.form.getAttribute('x-data')).form,
            {
                headers: {
                    'X-CSRF-Token': {
                        toString: () => this.$refs.form.querySelector('[name="_token"]').value,
                    }
                }
            }
        )
    },
    submit() {
        this.submitted = true
        this.form.submit()
            .then(response => {
                this.form.reset()
                this.$refs.form.reset()
                this.success = true
                this.submitted = false
                setTimeout(() => {
                    this.success = false
                }, 4500)
            })
            .then(this.$refs.form.scrollIntoView())
            .catch(error => {
                const summary = document.querySelector('#summary')
                if (summary) {
                    this.$focus.focus(summary.querySelector('a'))
                }
                else {
                    console.log(error)
                }
            })
    }
 });

image

danielreales7 avatar Sep 16 '24 10:09 danielreales7

When you visit one of your cached form pages, do you see an AJAX request to /!/nocache being done automatically?

jasonvarga avatar Sep 16 '24 14:09 jasonvarga

When you visit one of your cached form pages, do you see an AJAX request to /!/nocache being done automatically?

If I load a page cached, I can see: /!/nocache with status 200

When send the form to: /!/forms/contact 419 unknown status

danielreales7 avatar Sep 16 '24 14:09 danielreales7

@duncanmcclean I create a new project if you need test error: https://github.com/danielreales7/test-cache-form

I did a clean installation of Statamic 5 (with Peak Studio) without any configuration. If you try to send the form with all the fields empty, it shows the error messages correctly.

If you now add STATAMIC_STATIC_CACHING_STRATEGY=full to the .env, you cache the page, once cached, try to delete cookies from the browser and try to send the form.

You will see in the console:

image

danielreales7 avatar Sep 25 '24 23:09 danielreales7

https://github.com/user-attachments/assets/de76bd78-9d85-4a7a-a57a-d01173209b30

I am attaching a video of something I just realized. If it is sent without being cached, it shows the errors. When it is cached and I delete cookies, it does not show the errors. However, if I write on any input and send the form again, it shows the errors again.

danielreales7 avatar Oct 02 '24 20:10 danielreales7

It seems when a page is cached and you submit an empty form you get a token mismatch error. As soon as you start filling in fields stuff kicks back in.

robdekort avatar Oct 03 '24 15:10 robdekort

Is it possible you're submitting before the nocache JS has run and replaced the csrf token in the form?

ryanmitchell avatar Oct 04 '24 07:10 ryanmitchell

No. Nocache is loaded before submitting the form. If I don't use cache it works fine.

danielreales7 avatar Oct 04 '24 07:10 danielreales7

This issue has not had recent activity and has been marked as stale — by me, a robot. Simply reply to keep it open and send me away. If you do nothing, I will close it in a week. I have no feelings, so whatever you do is fine by me.

github-actions[bot] avatar Dec 04 '24 02:12 github-actions[bot]

Go away, bot.

robdekort avatar Dec 04 '24 09:12 robdekort