Aller au contenu principal

Module 9 – Sécurité et permissions

Niveau 5.1 – Laravel Livewire


Propriétés publiques = modifiables par le client. authorize() (policies) dans les actions et dans mount(), #[Locked] pour les propriétés sensibles. En fin de module : rate limiting, validation des paramètres de méthode, audit des points d’entrée.


Objectif

À la fin de ce module, vous saurez :

  • Protéger les actions sensibles (suppression, modification, accès à des données) avec les policies Laravel et $this->authorize() dans les méthodes du composant ou dans mount().
  • Valider toutes les entrées utilisateur (propriétés modifiées via wire:model ou paramètres passés aux méthodes) avec validate() ou #[Rule], et ne jamais faire confiance aux données envoyées par le client.
  • Comprendre que Livewire gère le CSRF automatiquement (chaque requête inclut le token) et savoir quelles propriétés ne doivent pas être modifiables par le client (#[Locked] ou non exposées).
  • Éviter les pièges : mass assignment sans validation, IDs sensibles dans l’état, et actions sans vérification d’autorisation.

Rappel : le client peut envoyer n’importe quoi

En Livewire, les propriétés publiques du composant sont sérialisées et renvoyées par le navigateur à chaque requête. Un utilisateur malveillant peut modifier le payload (outils de dev, requêtes forgées) pour changer les valeurs des propriétés (ex. mettre un user_id à un autre utilisateur, ou un role à « admin »). C’est pourquoi toute donnée entrante doit être validée côté serveur et toute action sensible doit être autorisée (policies, règles métier). Ne jamais faire confiance au client.


Autorisation avec les policies Laravel

Laravel fournit les policies pour centraliser la logique « qui peut faire quoi » sur un modèle (view, create, update, delete, etc.). En Livewire, vous appelez $this->authorize('action', $model) (ou $this->authorize('action', Model::class) pour les actions de type « viewAny », « create ») dans :

  • Une méthode d’action (ex. delete($id)) : avant d’effectuer l’opération, vous vérifiez que l’utilisateur courant a le droit.
  • mount() : pour un composant entier (ex. liste réservée aux admins). Si authorize lève une AuthorizationException, l’utilisateur reçoit une réponse 403 et le composant ne s’affiche pas (ou l’action n’est pas exécutée).

Exemple : autoriser la suppression dans une méthode

use App\Models\Post;

public function delete(int $id): void
{
$post = Post::findOrFail($id);
$this->authorize('delete', $post);
$post->delete();
session()->flash('message', 'Article supprimé.');
}
  • $this->authorize('delete', $post) : appelle la méthode delete() de la policy PostPolicy avec l’utilisateur courant et $post. Si la policy retourne false (ou lève une exception), Laravel renvoie 403 et $post->delete() n’est pas exécuté.

Exemple : autoriser l’accès au composant dans mount()

public function mount(): void
{
$this->authorize('viewAny', Post::class);
}

Seuls les utilisateurs qui passent viewAny sur Post peuvent voir ce composant (ex. liste d’articles en modération). Les autres reçoivent 403.

Créer une policy : php artisan make:policy PostPolicy --model=Post. Puis implémenter les méthodes viewAny, view, create, update, delete, etc. selon vos règles métier (rôle admin, propriétaire du post, etc.).


Validation : ne jamais faire confiance aux entrées

Toutes les données qui viennent du client (propriétés mises à jour via wire:model, paramètres passés à call() côté test, ou paramètres de mount() s’ils viennent de la requête) doivent être validées avant toute logique métier ou écriture en BDD.

  • Utilisez #[Rule] sur les propriétés et $this->validate() (ou validateOnly()) dans les méthodes qui modifient des données ou appellent Eloquent.
  • Ne jamais faire $this->fill($request->all()) (ou équivalent avec les propriétés du composant) sans règles de validation : cela permettrait un mass assignment de champs sensibles (ex. is_admin, role_id).
  • Si une propriété ne doit pas être modifiable par le client (ex. ID de l’entité en cours d’édition, fixé au chargement), utilisez #[Locked] (Livewire 3) pour qu’elle ne soit pas mise à jour depuis le payload, ou ne la mettez pas en public (et rechargez-la dans mount() ou hydrate()).

Exemple : formulaire d’édition d’un article. L’ID de l’article ne doit pas être modifiable par le client.

use Livewire\Attributes\Locked;
use Livewire\Attributes\Rule;

public int $postId; // passé en mount, ne pas exposer en wire:model
#[Locked]
public int $articleId = 0; // si vous devez le garder en public pour le rendu

#[Rule('required|string|min:3')]
public string $title = '';

public function mount(int $postId): void
{
$this->authorize('update', Post::findOrFail($postId));
$this->postId = $postId;
// charger title, etc.
}

CSRF

Livewire inclut automatiquement le token CSRF dans ses requêtes AJAX. Vous n’avez rien à faire de plus tant que votre application Laravel utilise le middleware VerifyCsrfToken (actif par défaut sur les routes web). Ne désactivez pas la vérification CSRF pour les routes Livewire.


Exemple : liste d’articles avec suppression autorisée

Composant qui affiche une liste d’articles et un bouton « Supprimer » par ligne. Seul l’auteur (ou un admin) peut supprimer.

public function delete(int $id): void
{
$post = Post::findOrFail($id);
$this->authorize('delete', $post);
$post->delete();
session()->flash('message', 'Article supprimé.');
}

Vue : bouton qui appelle delete avec l’ID (vérifier que l’ID vient bien du serveur, pas d’un champ éditable par l’utilisateur) :

<button wire:click="delete({{ $post->id }})" wire:confirm="Supprimer cet article ?">Supprimer</button>

wire:confirm (Livewire 3) affiche une boîte de confirmation avant d’envoyer l’action ; cela évite les clics accidentels mais ne remplace pas authorize() côté serveur.


À retenir

  • $this->authorize('action', $model) (ou authorize('viewAny', Model::class)) dans les méthodes d’action et/ou dans mount() pour protéger l’accès et les opérations sensibles.
  • Toujours valider les entrées : #[Rule] + validate() ; jamais de mass assignment sans règles. Les propriétés publiques sont modifiables par le client.
  • #[Locked] pour les propriétés qui ne doivent pas être modifiables par le client (ex. ID d’entité).
  • CSRF : géré automatiquement par Livewire ; ne pas désactiver la vérification.

Approfondissement

  • Rate limiting : les requêtes Livewire passent par Laravel ; vous pouvez appliquer un throttle sur les routes concernées ou dans le middleware pour limiter le nombre de requêtes par minute par utilisateur (éviter les abus sur les actions coûteuses).
  • Valider les paramètres des méthodes : une méthode delete(int $id) reçoit $id du client. Vérifiez que l’entité existe et que l’utilisateur a le droit : $post = Post::findOrFail($id) puis $this->authorize('delete', $post). Ne jamais faire confiance à $id (ex. un utilisateur pourrait envoyer l’ID d’un autre article).
  • Audit : pour chaque composant, lister les entrées (propriétés publiques, paramètres de mount() et des méthodes d’action) et s’assurer que chacune est validée ou autorisée. Les propriétés #[Locked] ne sont pas modifiables par le client mais restent dans le payload ; ne pas y mettre de données secrètes.

À retenir

  • authorize() dans les actions sensibles et dans mount() pour les pages réservées ; validate() sur toutes les entrées ; #[Locked] pour les propriétés non modifiables par le client.

Dans le prochain module, nous voyons la performance et l’optimisation : wire:key, debounce, lazy loading, et réduction du poids des requêtes.