statamic / cms

The core Laravel CMS Composer package
https://statamic.com
Other
3.88k stars 519 forks source link

CSRF token mismatch full cache strategy #10801

Open danielreales7 opened 2 weeks ago

danielreales7 commented 2 weeks ago

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 commented 2 weeks ago

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());
    }
duncanmcclean commented 1 week ago

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?

danielreales7 commented 1 week ago

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.

duncanmcclean commented 1 week ago

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?

danielreales7 commented 1 week ago

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 commented 1 week ago

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.

duncanmcclean commented 1 week ago

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

danielreales7 commented 1 week ago

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

jasonvarga commented 1 week ago

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

danielreales7 commented 1 week ago

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 commented 3 days ago

@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