Livewire Strict: Enforce additional security measures to Livewire

Livewire Strict: Enforce additional security measures to Livewire

Livewire Strict helps enforce security measures and prevents you from having unprotected sensitive public properties.

Did you know any public Livewire component property is open to manipulation? If not, now you know :)

Let me give you an example. We have a Livewire component that we include in our dashboard:

1<livewire:user-balance :user-id="auth()->id()">

The component itself looks something like this:

1class UserBalance extends Component
2{
3 public $userId;
4 
5 #[Computed]
6 public function balance()
7 {
8 return User::find($this->userId)->balance;
9 }
10 
11 public function render()
12 {
13 return view('livewire.user-balance');
14 }
15}

The $userId is set as a public property. Now, you might think that because the component is rendered on the back-end inside our Blade file, this component cannot be tampered with. This, however, is not the case. I can use the Livewire client library to manipulate the $userId using my browser dev tools and enter:

1Livewire.first().set('userId', 10);

This will grab the first Livewire component on the page and change the $userId to 10. Now, I am able to see the balance for this user.

To fix this, you can use the Locked attribute that is provided by Livewire:

1class UserBalance extends Component
2{
3 #[Locked]
4 public $userId;
5 
6 // ...
7}

You will have to ensure you lock your properties when needed. What if you could instead lock all properties by default and unlock specific properties you want users to be able to modify? This is where the Livewire Strict package comes in. With one line of code, you can lock all properties.

First, install the package:

1composer require wire-elements/livewire-strict

Next, all you have to do is call LivewireStrict::lockProperties() in the register() method of your AppServiceProvider.

1use WireElements\LivewireStrict\LivewireStrict;
2 
3class AppServiceProvider extends ServiceProvider
4{
5 /**
6 * Register any application services.
7 */
8 public function register(): void
9 {
10 LivewireStrict::lockProperties();
11 
12 // Only enable locally
13 LivewireStrict::lockProperties(shouldLockProperties: app()->isLocal());
14 }
15 
16 /**
17 * Bootstrap any application services.
18 */
19 public function boot(): void
20 {
21 //
22 }
23}

Now, all properties are locked by default for components that are part of the App\Livewire namespace. If you want to be more specific you can pass the components attribute and define specific components and/or namespaces:

1LivewireStrict::lockProperties(components: [MyComponent::class, 'SomeNamespaces/*']);

To "unlock" properties, you can choose to unlock at the component or property level:

1use WireElements\LivewireStrict\Attributes\Unlocked;
2 
3class Counter extends Component
4{
5 #[Unlocked]
6 public $count = 0;
7 
8 public function increment()
9 {
10 $this->count++;
11 }
12 
13 public function render()
14 {
15 return view('livewire.counter');
16 }
17}

In the example above, the public property $count can now be manipulated without throwing the CannotUpdateLockedPropertyException exception.

In some cases, you might have a component that contains a lot of public properties, so this would look like the following:

1use WireElements\LivewireStrict\Attributes\Unlocked;
2 
3class SignUp extends Component
4{
5 #[Unlocked]
6 public $firstName;
7 
8 #[Unlocked]
9 public $lastName;
10 
11 #[Unlocked]
12 public $email;
13}

If you want all public properties to be unlocked, you can add the attribute on the component class level instead:

1use WireElements\LivewireStrict\Attributes\Unlocked;
2 
3#[Unlocked]
4class SignUp extends Component
5{
6 public $firstName;
7 
8 public $lastName;
9 
10 public $email;
11}

That looks a bit cleaner and will have the same result.

So, if you are looking to minimize the chances for your Livewire components to be vulnerable, install the Livewire Strict package.

To navigate
Press Enter to select