Samen Steeve
Retour

2026-06-22

·3 min de lecture

Ce que la recherche en sécurité m'a appris sur la façon d'écrire du code

Quand tu passes du temps à chercher des failles dans des applications, tu regardes ton propre code différemment. Voici ce que ça change concrètement.

#Sécurité#Laravel#Bonnes pratiques#Architecture

La plupart des développeurs pensent à la sécurité comme à une étape — un audit à faire après avoir codé une feature. La recherche en sécurité t'apprend que c'est une façon de penser.

Le changement de perspective

Quand tu développes, tu penses à ce que le code devrait faire. Quand tu cherches des vulnérabilités, tu penses à ce que le code pourrait faire si quelqu'un l'utilise différemment de ce que tu avais prévu.

Ces deux postures changent radicalement la façon dont tu modélises les problèmes.

Les erreurs les plus fréquentes que j'observe

Faire confiance à l'entrée utilisateur trop tôt

// Dangereux — l'ID vient de la requête sans vérification de propriété
public function destroy(Request $request): RedirectResponse
{
    Document::findOrFail($request->id)->delete();
    return back();
}
// Sécurisé — on vérifie que l'utilisateur est propriétaire de la ressource
public function destroy(Document $document): RedirectResponse
{
    $this->authorize('delete', $document); // Laravel Policy
    $document->delete();
    return back();
}

La première version fonctionne parfaitement en dev. En production, n'importe quel utilisateur authentifié peut supprimer le document d'un autre en manipulant l'ID dans la requête — c'est une IDOR (Insecure Direct Object Reference).

Les mass assignment oubliés

// Risqué si le modèle n'a pas de $fillable défini correctement
User::create($request->all());

// Sécurisé — on définit explicitement ce qu'on accepte
User::create($request->only(['name', 'email', 'password']));

Si quelqu'un ajoute is_admin=1 dans la requête et que tu utilises $request->all(), le résultat est prévisible.

Les conditions de concurrence sur les ressources critiques

// Race condition possible — deux requêtes simultanées peuvent passer le check
if ($wallet->balance >= $amount) {
    $wallet->decrement('balance', $amount);
}

// Sécurisé — verrouillage pessimiste pendant la transaction
DB::transaction(function () use ($wallet, $amount) {
    $wallet->lockForUpdate()->find($wallet->id);

    if ($wallet->balance < $amount) {
        throw new InsufficientFundsException();
    }

    $wallet->decrement('balance', $amount);
});

Ce type de bug est invisible en tests unitaires et difficile à reproduire manuellement. C'est exactement le genre de faille qu'on découvre en cherchant activement.

Ce que ça change dans mon approche quotidienne

Je modélise les menaces avant de coder. Pour chaque fonctionnalité, je me pose la question : qui peut appeler cet endpoint ? Qu'est-ce qui se passe si deux utilisateurs font la même action au même moment ? Qu'est-ce qu'un utilisateur malveillant pourrait envoyer ?

Je traite les permissions comme des invariants, pas des vérifications ponctuelles. Plutôt que d'ajouter des if ($user->isAdmin()) partout, j'utilise des Policies Laravel qui centralisent la logique d'autorisation.

Je valide les transitions d'état. Un document ne passe pas de draft à published sans passer par review. Ces règles vivent dans des classes dédiées, pas dans des contrôleurs.

Je ne fais pas confiance aux données qui viennent de la base de données non plus. Si une valeur influence une décision critique — un rôle, un statut, un solde — elle est re-validée avant d'être utilisée.

La sécurité comme design, pas comme couche

La conclusion que j'ai tirée de ces expériences : la sécurité ne s'ajoute pas après. Elle s'intègre dans les décisions d'architecture dès le départ.

Un code bien structuré, avec des couches de responsabilités claires et des politiques d'autorisation centralisées, est naturellement plus difficile à compromettre. La sécurité et la qualité du code ne sont pas deux objectifs distincts — ils convergent vers le même endroit.