hunterhawley-45374036's avatar

What's the deal with Livewire and CSRF?

Hey there!

I recently spun up a new Laravel v12.4.1 app with Livewire v3.6.2 and am having trouble with 419 errors when using wire:click on a button.

I have a resources/views/thing-recorder/show.blade.php file that imports a layout found at resources/views/components/layouts/thing-recorder.blade.php. In the layout, I have the following in my <head>: <meta name="csrf-token" content="{{ csrf_token() }}"> along with @livewireStyles (@livewireScripts is the last thing before </body>).

In the show blade template, I bring in a Livewire component as such: <livewire:thing-recorder.panel :breakdown="$breakdown" />

In that component, I have a with wire:click="selectWorkflow('{{ $workflow->id }}')"

In app/Livewire/ThingRecorder/Panel.php I have a selectWorkflow method.

Here's the kicker: when I leave bootstrap/app.app as-is, I get a 419 error every time I try and click this button. However, I read online that you can modify it by adding this to the "withMiddleware" section to get around my problem: $middleware->validateCsrfTokens(except: ['livewire/*']);

When I add this, it works! However, it seems like this is really sketchy to disable CSRF in any case. With that said, I've read repeatedly online that "Livewire doesn't use CSRF," which is making me question if this is indeed sketchy or just necessary?

Thoughts?

0 likes
13 replies
Snapey's avatar

there is definitely something wrong with your install.

What url appears in your 419 error?

Check you are not including assets twice or Alpine on its own somewhere.

You can also get 419 errors if actually, your sessions are not working. Are you able to login?

1 like
hunterhawley-45374036's avatar

@Snapey I should have included this in the post -- yes, I am able to log in through standard Laravel auth, but (unless I disable in the middleware) I can't do anything in Livewire.

The URL in the browser does not change but in the console the 419 error is to: /livewire/update (I am using Herd on a .test but have also tried this on localhost by shutting Herd down and running php artisan serve and npm run dev, same result either way).

I searched the codebase/directory for @livewireScripts and @livewireStyles, I only found one instance of either; both in resources/views/components/layouts/thing-recorder.blade.php

I also searched the whole codebase/directory for any instance of alpine.js and found none.

1 like
vincent15000's avatar

Instead of the CSRF token, Livewire uses a checksum.

1 like
vincent15000's avatar

@hunterhawley-45374036 You don't need the CSRF token for all Livewire requests.

This is not a security issue for your application.

$middleware->validateCsrfTokens(except: ['livewire/*']);
hunterhawley-45374036's avatar

@vincent15000 it seems like they should ship the starter kit in Herd with this included if that’s the case then no? I am hoping to find a way to not have to do this and still get Livewire buttons to work. Is it your impression that it is not possible to get wire:click to work without this?

1 like
hunterhawley-45374036's avatar
Level 1

For anyone who may come across this in the future: it all went wrong when I set up UUID on my Users table. I didn't also update the sessions table foreign key to look for a UUID. WOW that was frustrating but I hope this helps someone.

3 likes
Snapey's avatar

And anyone reading this, DONT mess with CSRF. Its just not relevant for Livewire.

4 likes
hunterhawley-45374036's avatar

@Snapey 100% agree that CSRF settings should not be messed with to get Livewire to work, you absolutely should not have to disable it or anything like that to get it to work and there are security risks with doing so.

I would say though that CSRF is relevant for Livewire in that if you study the Network requests that are fired off when you use Livewire (/livewire/update), they do utilize CSRF validation out-of-the-box (which is what makes 419 errors in this case even possible in the first place). However, that CSRF validation will work just fine provided you have your sessions table set up correctly (unlike me). If you're getting 419's when using Livewire, the move definitely is not to disable CORs, you've got another problem.

2 likes
Darkdawg's avatar

Sorry for resurrecting a 7-month-old thread, but I was hoping you or someone could clarify a few things.

I'm experimenting with Livewire components on public, non-auth pages (same HTML for all users). From what I understand, these routes don't really benefit much from CSRF protection, right? My impression is that CSRF is mainly meant to prevent unwanted actions on behalf of an authenticated user, so for pages that don't have any user-specific state or actions, I'm not sure what it would be protecting against.

My goal is to fully cache these pages as static .html. The problem is that Livewire injects a CSRF token at the end of the <body>, and because the token is session-specific, it breaks the cache. If I strip the token out of the cached page, Livewire stops working whenever it tries to make updates.

I'm fairly sure the CSRF token is unnecessary for completely public pages with no user-specific state - but I might be wrong. Would it be a bad idea to disable CSRF verification for these specific routes so that Livewire can work without injecting a per-user token?

Any insight would be appreciated!

1 like
Snapey's avatar

if they are static, they can be cached, and you don't need to use Livewire, there is no benefit.

If the pages are dynamic then csrf will be used whenever data is posted to the backend, and you cannot cache.

1 like
Darkdawg's avatar

I think you're missing my point.

A page becomes dynamic (in the caching sense) only when the HTML output varies per user or per request. Livewire itself doesn't magically make a page dynamic - it's the session-bound CSRF token injected into the HTML that does. Is a page dynamic just because it has a <form>?

Yes, Livewire requires PHP to handle its update requests, but that happens after the page is loaded. My point is strictly about the initial HTML response. That response could theoretically be fully cacheable if it didn't include per-user data like the CSRF token. All subsequent Livewire requests would still hit Laravel normally.

My theory is that the CSRF token is unnecessary for public, non-auth pages, because there is nothing to protect against. Without the token the page can be fully cached, and you still get to keep all the wonderful things Livewire gives you. The SPA navigation and the server-driven reactive forms for example.

As far as I know, there's nothing a malicious site could achieve through a public Livewire component that it couldn't already do by just requesting the same public endpoint directly. Since there's no authenticated state involved, CSRF doesn't actually block any meaningful attack here.

I may be missing something about how Livewire internally depends on CSRF, so that's what I'm trying to understand better.

1 like

Please or to participate in this conversation.