Security by design in Laravel.
Waarom beveiliging geen feature is die je later toevoegt, maar een ontwerpprincipe dat je vanaf het eerste commit doorvoert. Over threat modeling, Laravel’s beveiligingslagen en de praktijk van veilig bouwen.
Wat is security by design?
Security by design betekent dat je beveiliging niet als losse feature toevoegt aan het einde van een project, maar als ontwerpprincipe meeneemt in elke architectuurbeslissing. Vanaf de eerste database-migratie, de eerste route, het eerste formulier. Het is het verschil tussen een huis bouwen met een stevig slot op de deur en een huis dat vanaf de fundering is ontworpen om inbraakbestendig te zijn.
In de praktijk betekent dit dat je bij elke feature jezelf afvraagt: wie mag dit zien? Wat gebeurt er als iemand kwaadwillend invoer stuurt? Welke data lekt er als deze endpoint openbaar wordt? Security by design is geen extra werk — het is de juiste manier van werken. En het bespaart je enorme kosten ten opzichte van achteraf patchen.
Beveiliging achteraf toevoegen is als een gordel monteren nadat de auto al gecrasht is. Security by design bouwt de airbags in vóór de eerste testrit.
Threat modeling: weten waar je kwetsbaar bent
Security by design begint niet met code, maar met nadenken. Threat modeling is het proces waarbij je systematisch in kaart brengt waar je applicatie aangevallen kan worden. Welke data is waardevol? Wie zijn potentiële aanvallers? Wat zijn de gevolgen als het misgaat?
Bij Coding Agency doorlopen we dit proces bij de start van elk project. We identificeren de trust boundaries — de grenzen waar data van de ene context naar de andere gaat. Denk aan: gebruikersinvoer die in de database belandt, API-responses die naar een frontend gaan, webhooks die van externe services binnenkomen. Op elk van die grenzen moet je valideren, sanitizen en autoriseren.
Een concreet voorbeeld: een SaaS-platform met multi-tenancy. De threat model identificeert direct het risico dat tenant A data van tenant B kan inzien. Dat risico bepaalt vervolgens de architectuur: global scopes op Eloquent-modellen, middleware die de tenant-context afdwingt, en tests die expliciet cross-tenant toegang proberen.
Laravel’s beveiligingslagen
Laravel is niet toevallig ons framework van keuze. Het biedt meerdere beveiligingslagen die — mits correct toegepast — een solide fundament vormen. Maar een framework beschermt je alleen als je begrijpt wat het doet en waar de grenzen liggen.
Autorisatie als architectuur
Laravel’s Gate en Policy systeem maakt het mogelijk om autorisatielogica te centraliseren. In plaats van ad-hoc if-statements in controllers definieer je Policies per model. Elke actie — bekijken, aanmaken, wijzigen, verwijderen — krijgt een expliciete autorisatiecheck. Dit voorkomt de meest voorkomende kwetsbaarheid uit de OWASP Top 10: Broken Access Control.
Het principe is simpel: deny by default. Niets is toegankelijk tenzij je het expliciet toestaat. Een nieuwe endpoint zonder Policy? Dan is die geblokkeerd. Dit is het tegenovergestelde van de veel voorkomende fout waarbij alles standaard open is en je hoopt dat je niets vergeet af te schermen.
Input-validatie als eerste verdedigingslinie
Elke request die binnenkomt is potentieel vijandig. Laravel’s Form Request classes bieden een elegante manier om validatie te centraliseren en af te dwingen vóórdat je controller-logica überhaupt bereikt wordt. Je definieert niet alleen wat verplicht is, maar ook wat het datatype moet zijn, welke waarden acceptabel zijn en welke relaties geldig zijn.
Een goed ontworpen validatielaag is meer dan required|string. Het is: required|string|max:255|not_regex:/[<>]/. Het is exists:teams,id in combinatie met een scope die controleert of de gebruiker daadwerkelijk lid is van dat team. Input-validatie is niet alleen een UX-feature — het is een beveiligingsmaatregel.
Mass assignment bescherming
Laravel beschermt standaard tegen mass assignment — het onbedoeld toewijzen van velden via request-data. Met $fillable of $guarded op je Eloquent-modellen bepaal je precies welke velden via formulieren of API’s gewijzigd mogen worden. Zonder deze bescherming kan een aanvaller extra velden meesturen — zoals is_admin=1 — en zo rechten escaleren.
Encryptie en hashing
Gevoelige data hoort versleuteld opgeslagen te worden. Laravel biedt hier twee mechanismen voor. Hashing voor wachtwoorden: bcrypt of Argon2, niet omkeerbaar, met automatische salt. Encryptie voor data die je later weer moet kunnen lezen: AES-256-CBC via Laravel’s encrypt() en decrypt() helpers, of de encrypted cast op Eloquent-attributen.
Het onderscheid is cruciaal. Wachtwoorden worden gehasht — je vergelijkt hashes, je slaat nooit het origineel op. Persoonsgegevens, API-tokens en sessiedata worden versleuteld — je kunt ze teruglezen, maar alleen met de juiste sleutel. Laravel maakt het makkelijker om dit goed te doen dan om het fout te doen.
Veelgemaakte fouten (en hoe je ze voorkomt)
Security by design draait niet alleen om wat je wél doet, maar ook om wat je vermijdt. Dit zijn de fouten die we in de praktijk het vaakst tegenkomen bij codebases die we overnemen of reviewen:
- Route model binding zonder scoping. Laravel’s route model binding is handig, maar zonder scope kan een gebruiker door URL-parameters te raden bij andermans records komen. Gebruik altijd scoped bindings of voeg een Policy-check toe.
- Raw queries zonder parameter binding. Laravel’s Eloquent beschermt standaard tegen SQL-injection, maar zodra je
DB::raw()ofwhereRaw()gebruikt zonder parameter binding, gooi je die bescherming weg. - Gevoelige data in logs. Laravel logt standaard exceptions, maar als een exception request-data bevat met wachtwoorden of tokens, staat die data ineens in je logbestanden. Configureer altijd welke velden geëxcludeerd worden via
$hiddenop modellen en het masking van request-parameters. - API-responses die te veel teruggeven. Een
User::all()in een API-response stuurt alle kolommen mee — inclusief password hashes, remember tokens en interne velden. Gebruik altijd API Resources om expliciet te bepalen welke velden zichtbaar zijn. - Ontbrekende rate limiting. Zonder rate limiting op login-endpoints, wachtwoord-resets en API’s is je applicatie kwetsbaar voor brute-force aanvallen. Laravel’s
RateLimitermaakt dit triviaal om te implementeren. - Debug-modus in productie. Een
APP_DEBUG=truein productie onthult stack traces, database-configuratie en environment variables aan elke bezoeker. Een enkel foutief geconfigureerd .env-bestand kan je volledige infrastructuur blootleggen.
Security headers en browser-bescherming
Een veilige applicatie stuurt de juiste instructies mee naar de browser. Security headers vertellen de browser hoe hij je applicatie moet behandelen en welke risico’s hij moet blokkeren. Dit is een verdedigingslaag die vaak vergeten wordt, maar die veel gangbare aanvallen effectief voorkomt.
Content Security Policy (CSP) bepaalt welke bronnen de browser mag laden. Geen inline scripts, geen externe fonts van onbekende domeinen, geen iframes van derden. Dit is je krachtigste wapen tegen XSS-aanvallen — zelfs als een aanvaller erin slaagt een script te injecteren, blokkeert de CSP de uitvoering ervan.
Strict-Transport-Security (HSTS) dwingt HTTPS af en voorkomt dat de browser ooit een onversleutelde verbinding maakt. X-Content-Type-Options voorkomt MIME-sniffing. X-Frame-Options beschermt tegen clickjacking door te voorkomen dat je applicatie in een iframe wordt geladen.
In Laravel configureer je deze headers via middleware die op elke response wordt toegepast. Eenmalig instellen, altijd beschermd.
Dependency management als security-maatregel
Je applicatie is zo veilig als je zwakste dependency. Een verouderde package met een bekende kwetsbaarheid is een open deur voor aanvallers. Security by design betekent dat je je dependencies actief beheert.
Wij gebruiken composer audit en npm audit als onderdeel van onze CI/CD-pipeline. Elke build controleert of er bekende kwetsbaarheden zitten in de gebruikte packages. Bij een kritieke vulnerability wordt de build geblokkeerd totdat de dependency is geüpdatet.
Daarnaast hanteren we het principe van minimal dependencies. Elke package die je toevoegt vergroot je aanvalsoppervlak. Voordat we een dependency toevoegen, vragen we: kunnen we dit zelf in een paar regels oplossen? Wordt deze package actief onderhouden? Heeft deze package een goede security-track record?
Hoe wij security by design toepassen
Bij Coding Agency is security by design geen theoretisch concept maar dagelijkse praktijk. Het zit in ons ontwikkelproces verweven:
Architectuurfase. Threat modeling bij elk nieuw project. We identificeren aanvalsoppervlakken, bepalen welke data gevoelig is en ontwerpen de autorisatiearchitectuur voordat er een regel code geschreven wordt.
Development. Elke feature wordt gebouwd met autorisatie, validatie en encryptie als standaard. Code reviews bevatten een expliciete security-checklist. Nieuwe endpoints zonder Policy worden niet gemerged.
Testing. Naast functionele tests schrijven we security-gerichte tests. Kan een gebruiker met role A data van role B benaderen? Worden rate limits correct afgedwongen? Wordt gevoelige data geëxcludeerd uit API-responses?
Deployment. Security headers, environment-validatie en dependency audits zijn onderdeel van de CI/CD-pipeline. Niets gaat live zonder deze checks.
Onderhoud. Dependencies worden proactief bijgewerkt. Security advisories worden gemonitord. En periodieke pentests valideren dat onze aanpak standhoudt tegen nieuwe aanvalstechnieken.
Security by design is geen checklist die je afwerkt. Het is een mindset die je toepast bij elke regel code die je schrijft.
Wil je weten hoe wij security by design toepassen in jouw project? Of wil je een bestaande applicatie laten reviewen op beveiligingsrisico’s? Neem contact op voor een vrijblijvend gesprek.
/Gerelateerde artikelen
Veiligheid en pentests
Hoe wij security inbouwen en waarom onze software pentests doorstaat.
Twee-factor authenticatie (2FA/MFA)
Waarom één wachtwoord niet genoeg is en hoe je je applicatie beveiligt.
AVG / GDPR voor webapplicaties
De technische vereisten voor AVG-compliance in je webapplicatie.