DEV Community

Cover image for Stop Exposing State: Auto-Lock Your Livewire 4 Properties From Client-Side Tampering
Jane Code Life
Jane Code Life

Posted on

Stop Exposing State: Auto-Lock Your Livewire 4 Properties From Client-Side Tampering

Laravel Livewire is absolute magic for building reactive interfaces. However, with great reactivity comes great security responsibility. A very common security oversight for developers is leaving backend component public properties exposed to client-side manipulation.

Today, I want to talk about how this happens in Livewire 4 and how we can enforce a "Secure by Default" pattern with zero effort.


🚨 The Blind Spot: Client-Side Tampering

By default, all public properties in a Livewire component are part of the component's state and can be modified from the front-end (via browser dev tools or intercepted requests).

Consider this innocent-looking component:

class UserDashboard extends Component {
    public string \$name = 'Jane Doe';
    public string \$role = 'member'; // ⚠️ Vulnerable!

    // ...
}
Enter fullscreen mode Exit fullscreen mode

If you don't explicitly validate or authorize every single request touching $role, a malicious user can easily payload-inject role = 'admin' through their browser. Remembering to protect every sensitive property across dozens of files creates huge cognitive load and introduces human error.


✅ The Elegant Solution: Auto-Lock Everything

To solve this pain point entirely, I built and published an open-source package: Livewire Secure Properties.

It completely flips the default security model of Livewire 4: Everything is locked and immutable from the client-side unless you explicitly mark it as unlocked.

You no longer have to guess what's exposed. Your backend state stays secure out of the box, throwing a 403 PropertyLockedException the moment any unauthorized client-side update is attempted.

🛠️ Installation

You can drop this into your Livewire 4 project instantly via Composer:

composer require janecodelife/livewire-secure-properties
Enter fullscreen mode Exit fullscreen mode

🔓 How to Use It

The package offers a zero-configuration experience. It fully supports both Livewire 4's native Single File Components (SFC) and traditional Class-based layouts.

To open up specific properties for data-binding (like form inputs bound to wire:model), simply apply the #[Unlocked] attribute:

1. Single File Components (SFC)

<?php
use Livewire\Component;
use JaneCodeLife\LivewireSecureProperties\Unlocked;

new class extends Component {
    // 🔒 SECURED: Auto-locked by default. Front-end manipulation triggers a 403 error.
    public string \$role = 'admin';

    // 🔓 UNLOCKED: Safe and open for client-side wire:model updates
    #[Unlocked]
    public string \$name = 'Jane Doe';
};
?>

<div>
    <input type="text" wire:model.live="name">
    <p>Name: {{ \$name }}</p>
</div>
Enter fullscreen mode Exit fullscreen mode

2. Multiple File Components (Class-based)

use Livewire\Component;
use JaneCodeLife\LivewireSecureProperties\Unlocked;

class UserProfile extends Component
{
    public string \$userId = '12345'; // 🔒 Locked by default

    #[Unlocked]
    public string \$bio = 'Software Engineer'; // 🔓 Unlocked for editing

    public function render() {
        return view('livewire.user-profile');
    }
}
Enter fullscreen mode Exit fullscreen mode

🔗 Repo Link


🔗 My Other Plugins

Check out my other open-source tools to supercharge your Neovim environment:

Top comments (0)