Je hebt geen security-afdeling nodig om veilig op te leveren. Je hebt een klein aantal vaste gewoontes nodig, en tooling die ze afdwingt.
Je hebt geen security-afdeling nodig om veilig op te leveren. Je hebt een klein aantal vaste gewoontes nodig, en tooling die ze afdwingt.
Wij zijn een klein team senior developers. Geen security-team, geen aparte QA-laag, geen projectmanager die een proces bewaakt. Toch leveren we Magento-, Shopify- en Laravel-projecten op die een audit doorstaan. Dat lukt omdat de belangrijkste checks geautomatiseerd zijn en de rest in een korte review-routine zit.
Hieronder lees je hoe zo'n opzet eruitziet. Het is bruikbaar voor ons, voor jouw interne team of als vraag aan het bureau dat jouw webshop bouwt.
Het principe: automatiseer wat kan, review wat moet
Een klein team heeft weinig tijd. Dus verdeel je het werk zo:
- Machines vangen de herhaalbare fouten: kwetsbare dependencies, gelekte secrets, bekende gevaarlijke patronen, type-fouten.
- Mensen beoordelen wat een machine niet snapt: toegangslogica, bedrijfsregels, ontwerpkeuzes.
Een reviewer die zijn tijd kwijt is aan "je mist een null-check" heeft geen aandacht meer voor "deze endpoint laat iedere ingelogde gebruiker elke order opvragen". Laat tools het eerste doen.
Laag 1: code review met een korte security-checklist
Elke wijziging naar main gaat via een pull request, bekeken door een tweede persoon. Ook als je haast hebt. Zeker als je haast hebt.
Een review-checklist die in een minuut gelezen is, werkt beter dan een document van tien pagina's. Dit staat bij ons in het pull request-template:
- Toegang. Wie mag dit aanroepen? Wordt dat server-side gecontroleerd, niet alleen in de interface? Kan gebruiker A bij gegevens van gebruiker B door een ID te wijzigen?
- Input. Wordt alle invoer gevalideerd? Gaan queries via parameters of de ORM, nooit via string-concatenatie?
- Output. Wordt uitvoer in de juiste context geëscaped? Is er nieuwe
{!! !!},|rawofinnerHTML? - Secrets. Staat er een sleutel, token of wachtwoord in de code, config of testdata?
- Bestanden. Worden uploads gecontroleerd en buiten de webroot of zonder uitvoerrechten opgeslagen?
- Nieuwe dependency. Is die nodig, onderhouden en gecontroleerd? Zie software supply chain.
- Foutafhandeling. Lekt een foutmelding interne details? Faalt de code veilig (toegang weigeren bij twijfel)?
- Logging. Wordt een gevoelige actie vastgelegd, zonder gevoelige data in de log?
- Betaling en checkout. Raakt dit scripts of velden op de betaalpagina? Dan extra aandacht, zie PCI DSS 4.0.
Regels voor het proces:
- Wijzigingen aan authenticatie, autorisatie, betalingen en de pipeline krijgen altijd een grondige review.
- Kleine pull requests. Een reviewer die 2000 regels moet lezen, keurt goed zonder te lezen.
- De auteur kan niet zijn eigen wijziging goedkeuren, ook niet als hij de enige senior is. Dan vraagt hij een collega of een externe reviewer.
Laag 2: statische analyse met PHPStan en Psalm
Statische analyse leest je PHP zonder het uit te voeren en vindt typefouten, onmogelijke condities en niet-afgehandelde gevallen. Dat lijkt geen security, maar veel kwetsbaarheden beginnen als een type-fout of een onverwachte null.
- PHPStan is de bekendste. Voor Laravel is er een extensie die Eloquent en facades begrijpt.
- Psalm is een alternatief en heeft een ingebouwde taint analysis: het volgt data van een bron (zoals
$_GET) naar een gevaarlijke bestemming (zoals een SQL-query) en meldt als er geen sanering tussen zit.
Praktische tips:
- Begin op een niveau dat je projecten aankunnen en verhoog dat langzaam. Een baseline-bestand zet bestaande fouten opzij, zodat nieuwe code wel streng wordt beoordeeld.
- Laat de CI falen bij nieuwe fouten. Een waarschuwing die niemand leest, doet niets.
- Magento-code met veel dynamische generatie (factories, plugins, interceptors) geeft ruis. Richt je op je eigen modules en gebruik de Magento-specifieke ondersteuning of een passende configuratie.
vendor/bin/phpstan analyse --memory-limit=1G
Laag 3: SAST met Semgrep
SAST (static application security testing) zoekt naar onveilige patronen in broncode. Semgrep is daarvoor een praktische keuze voor kleine teams: regels lezen als de code zelf, en je kunt ze eenvoudig uitbreiden.
Wat je ermee vindt:
- gebruik van
eval,unserializeop onbetrouwbare data,exec/shell_execmet variabelen - raw SQL met geconcateneerde variabelen
- uitgeschakelde TLS-controle in HTTP-clients
- gevaarlijke patronen zoals
innerHTMLmet onbetrouwbare data - hardcoded secrets (deels)
Eerlijk over de beperkingen:
- SAST geeft false positives. Als elke run veertig meldingen oplevert waarvan er drie echt zijn, schakelt je team het uit. Begin met een kleine, nauwkeurige regelset en breid uit.
- SAST begrijpt je bedrijfslogica niet. Een endpoint zonder toegangscontrole ziet er syntactisch correct uit.
- Het vervangt geen review en geen pentest.
Een zinvolle aanpak: draai Semgrep op elk pull request, laat alleen nieuwe bevindingen blokkeren, en bekijk de rest periodiek.
Laag 4: dependency-checks
Het grootste deel van je code is niet van jou. Zie ook Magento-extensies veilig installeren.
composer audit
npm audit --omit=dev
composer audit(Composer 2.4 en hoger) vergelijkt je packages met bekende advisories.- Draai het bij elk pull request en ook op een schema, omdat nieuwe kwetsbaarheden verschijnen zonder dat jouw code verandert.
- Gebruik een tool als Dependabot of Renovate voor automatische update-voorstellen, in kleine pull requests die door de pipeline worden getest.
- Commit lockfiles en gebruik
composer installennpm ciin CI en productie.
Een gemelde kwetsbaarheid in een package betekent niet altijd dat jij kwetsbaar bent. De vraag is of je de kwetsbare functie gebruikt. Maar beoordeel dat bewust, schrijf op waarom je iets laat staan en kom erop terug.
Laag 5: secrets-scanning
Een uitgelekt API-token in een repository is een van de simpelste en meest voorkomende fouten.
- Gebruik een secrets-scanner (bijvoorbeeld Gitleaks of TruffleHog) als pre-commit hook en in CI. De hook voorkomt dat het erin komt, CI vangt wat de hook mist.
- Scan ook de git-geschiedenis eenmalig voor bestaande repositories.
- Houd
.envenauth.jsonbuiten git. - Is er toch iets gelekt? Roteer het secret. Verwijderen uit de repository is niet genoeg, want de geschiedenis en eventuele forks houden het.
Laag 6: staging met gemaskeerde productiedata
Een staging-omgeving met een kopie van de productiedatabase is handig en gevaarlijk. Klantgegevens op een slechter beveiligde omgeving zijn een datalek in wording. Dat is ook een AVG-kwestie.
Dit doen we in plaats daarvan:
- Maskeer persoonsgegevens bij het kopiëren: namen, e-mailadressen, telefoonnummers, adressen, betaalgegevens. Vervang door neppe maar realistische waarden.
- Haal e-mailadressen weg of leid ze om, zodat staging nooit echte klanten mailt.
- Gebruik testmodus van betaalproviders, nooit live-sleutels op staging.
- Zet staging achter authenticatie (IP-beperking of wachtwoord), zodat zoekmachines en bots er niet bij kunnen.
- Gebruik andere secrets dan op productie.
- Ruim oude dumps op. Een databasedump op een laptop is ook productiedata.
Een eenvoudige maskering is al veel waard. Perfect hoeft niet; de grootste risico's zijn onbeschermde e-mailadressen en betaalgegevens.
Laag 7: deploys met minimale rechten
De pijplijn is zelf een doelwit, zie het artikel over de supply chain. Kort:
- Deployen gebeurt via CI, niet vanaf een laptop.
- Eén deploy-key met alleen de rechten die hij nodig heeft, per omgeving.
- De applicatie draait onder een gebruiker die niet kan schrijven naar de eigen code. Schrijfrechten alleen waar nodig, zoals cache, sessies en uploads.
- De databasegebruiker van de applicatie kan geen schema's aanpassen of andere databases lezen. Migraties draaien met een apart account.
- Persoonlijke accounts op servers, geen gedeelde wachtwoorden. Toegang weghalen als iemand vertrekt.
- Bij Magento: productiemodus,
app/etc/env.phpniet leesbaar voor anderen, admin beschermd zoals in Magento admin beveiligen.
Beperkte rechten maken niet alle aanvallen onmogelijk, maar wel de schade kleiner. Een geslaagde RCE op een applicatie die niet naar zijn eigen code kan schrijven, is een stuk lastiger te laten overleven.
Hoe het eruitziet in de pipeline
Een compact beeld van wat bij elke pull request draait:
| Stap | Tool | Blokkeert merge bij |
|---|---|---|
| Stijl en typen | PHPStan of Psalm | Nieuwe fouten |
| Tests | PHPUnit / Pest | Falende tests |
| SAST | Semgrep | Nieuwe bevindingen met hoge zekerheid |
| Dependencies | composer audit, npm audit | Bekende kwetsbaarheden zonder uitzondering |
| Secrets | Gitleaks of vergelijkbaar | Elke vondst |
| Review | Een tweede persoon | Ontbrekende goedkeuring |
Deze set kost een paar uur om op te zetten en draait vanzelf. Het is geen volledige beveiliging. Het is de ondergrens die bij elk project hoort.
Wat je niet hoeft te doen
Eerlijk is ook: sommige dingen zijn voor een klein team overkill.
- Een eigen security operations center. Gebruik een goede logdienst en een paar alerts, zie security logging.
- Alle tools tegelijk invoeren. Begin met dependency-checks, secrets-scanning en review. Voeg SAST en statische analyse toe zodra de basis loopt.
- Threat modelling op elke kleine wijziging. Doe het bij nieuwe functies met gevoelige data of nieuwe koppelingen.
Wat niet in je team zit, kun je inkopen: een periodieke pentest of security audit door iemand die jouw code niet schreef. Interne controle vindt veel, een tweede paar ogen vindt de rest. Voor maatwerkcode is het ook handig om vooraf eisen vast te leggen, zie OWASP ASVS.
Veelgestelde vragen
Hebben we als klein team echt SAST nodig?
Niet per se meteen. Begin met code review, composer audit en secrets-scanning, want die leveren het meeste per uur werk. SAST voeg je toe als de basis loopt en je team tijd heeft om de meldingen te beoordelen.
Wat is het verschil tussen PHPStan en Semgrep?
PHPStan en Psalm analyseren types en logica en vinden fouten die vaak ook security-impact hebben. Semgrep zoekt gericht naar onveilige patronen. Ze vullen elkaar aan. Psalm kan daarnaast data volgen van input naar gevaarlijke functies (taint analysis).
Is een kopie van productie op staging toegestaan?
Alleen als je persoonsgegevens maskeert en de omgeving net zo goed beschermt als productie. Zonder maskering is staging met echte klantdata een datalek-risico en een AVG-probleem. Maskeren is de veiligere standaard.
Hoe vaak moeten we dependencies updaten?
Security-updates direct, andere updates in een vaste cadans, bijvoorbeeld maandelijks. Kleine, regelmatige updates zijn goedkoper en veiliger dan één grote sprong na een of twee jaar.
Kan code review een pentest vervangen?
Nee. Review vangt fouten in ontwerp en logica tijdens het bouwen. Een pentest toetst van buiten wat een aanvaller echt kan bereiken. Je wilt beide, op verschillende momenten.
Wil je weten hoe wij projecten bouwen en opleveren, of wil je dat iemand meekijkt naar de ontwikkelstraat van je eigen team? Neem contact op via /contact of bekijk onze security-aanpak.

Geschreven door Ruthger Idema
15+ jaar ervaring in e-commerce development. Gespecialiseerd in Magento, Shopify en Laravel maatwerk.
Meer over ons team →


