Valkuilen in Software Security
En hoe ze te voorkomen middels het toepassen van basisprincipes
En hoe ze te voorkomen middels het toepassen van basisprincipes
Wilco Kasteleijn, 17 december 2025
Wat zijn nu echt veel voorkomende problemen, naast de eeuwige SQL injection, cross-site scripting en outdated libraries waar je CI/CD waarschijnlijk al tegen beschermt? We kennen allemaal de gebruikelijke security maatregelen zoals authenticatie en autorisatie, OAuth2, SAML, MFA, CI/CD scanning: Github AS, SAST, DAST en Software Compositie Analyse (SCA). Dit zijn maatregelen die bij veel moderne projecten doorgaans wel zijn ingericht. Helaas dekken al deze maatregelen slechts een beperkt deel van de risico’s binnen de softwareontwikkelings-levenscyclus. Veel problemen ontstaan juist door voor de hand liggende (menselijke) fouten die worden gemaakt voor, tijdens en na het ontwikkelproces. Het zijn aspecten waar je als software engineer niet gauw geneigd bent aan te denken, denkt dat het niet jouw verantwoordelijkheid is, of dat het wel meevalt met de risico's. Deels omdat tijdens het ontwikkelingsproces daar de focus niet op ligt, deels vanwege tijdsdruk, maar ook gebrek aan bewustzijn.
NB: Dit blogartikel is geschreven n.a.v. een presentatie die ik over dit onderwerp gaf op het Navara kennisfestival van 9 oktober dit jaar.
Een paar jaar geleden volgde ik een specialisatie "Secure Coding Practices" via Coursera. Het bestond uit een aantal boeiende trainingen over Kali linux, Pentesting tools (Burp, OWASP ZAP), encryptie algoritmen en ook een wat stoffige training gegeven door een professor van de Davis University in Californië over "Security Principles", met wat gedateerde voorbeelden in C-code. Zoals gezegd komt men in de praktijk vaak voor de hand liggende zaken tegen. Door mezelf de vraag stellen hoe dat heeft kunnen ontstaan, werd mij duidelijk dat juist de lessen van die stoffige professor eigenlijk toch het meest waardevol bleken te zijn. Diezelfde basisprincipes komen ook weer geregeld terug als onderwerp in de ISC2 training voor het “Certified Secure Software Lifecycle Professional (CSSLP)” certificeringstraject dat ik enkele jaren geleden heb behaald. Voor degenen die net als ik geïnteresseerd zijn in dit onderwerp, kan ik deze training en certificering overigens aanbevelen.
Ik realiseerde mij dat elke software engineer vertrouwd zou moeten zijn met die basiskennis over security principes en die eigenlijk bij elke fase van een project in zijn achterhoofd heeft.
In het dagelijks werk als software engineer waar je continu bezig bent met gevoelige data is het ook belangrijk om je bewust te zijn over de mate van gevoeligheid van die data, waar en hoe wordt het opgeslagen, waar gaat het heen en waar het mis kan gaan. Wij als ontwikkelaars hebben de verantwoordelijkheid om ervoor te zorgen dat onze code op een veilige manier omgaat met deze data. Het klinkt misschien als een open deur, maar ik kan je verzekeren dat bijna iedereen (mezelf inbegrepen) toch neigt om zaken over het hoofd te zien, gewoon omdat je er op het moment dat je ermee bezig bent vaak niet bewust van bent. Je focust je vaak vooral op het oplossen van business use cases, en de non-functionele aspecten zoals security komen in dat proces nog weleens in het gedrang onder tijdsdruk en prioriteiten, met alle gevolgen van dien.
Na het lezen van deze blog hoop ik dat ook jij je hierin verder gaat verdiepen, zodat je in het dagelijks werk in staat bent om een andere bril op te zetten en uiteindelijk een intuïtie ontwikkelt zodat dit soort fouten niet per se worden gemaakt. Hoe? Door in elk geval een aantal van die basisprincipes van security in deze blog te benoemen. Ik kan uiteraard in deze korte blog niet alles behandelen maar hoop jullie in elk geval te inspireren om je er verder in te verdiepen.
Security gaat binnen de context van informatietechnologie in essentie over vertrouwelijkheid, integriteit en beschikbaarheid van informatie (CIA). In het volgende benoem ik een aantal basisprincipes die zorgen voor waarborging van deze drie begrippen. Hieronder zijn de belangrijkste weergegeven:
Navara kennisfestival op 9 oktober 2025
De meeste zijn bedacht door 2 MIT wetenschappers, Jerome Saltzer en Michael Schroeder in hun publicatie uit 1975 over informatiebeveiliging, en die tot op de dag van vandaag nog steeds de basis vormt voor informatiebeveiliging. Ik ga in deze blog niet in detail in op elk van deze principes maar zal er een paar benoemen en wat voorbeelden geven van gerelateerde problemen die ik in de praktijk ben tegengekomen.
Dit is waarschijnlijk het meest bekende waar eigenlijk iedereen wel van heeft gehoord. Dit gaat over wie toegang heeft tot welke modulen van een systeem (vaak m.b.v. RBAC). Wat ik vaak zie is dat dit een "alles of niets" principe is, dus of je hebt volledige toegang of inzage, of helemaal geen. Hierbij is er dus wel authenticatie gerealiseerd maar ontbreekt de autorisatie.
Een voorbeeld waar Least privilege wordt gebroken is wanneer secrets zoals tokens en credentials in configuratie staan in plaats van een Secrets Manager. Iedereen die bij de configuratie kan (ontwikkelaars, beheerders etc), kan erbij. Maar ontwikkelaars willen we niet voorzien van de productie wachtwoorden, en niet iedereen bij systeembeheer moet per definitie overal bij kunnen. Een ander voorbeeld: logs waarin zeer persoonlijke gegevens van mensen direct na te lezen zijn middels het observability platform. Iedereen met toegang tot de productie-logs kan deze gegevens inzien.
Het falen van een enkel component moet niet resulteren in het falen van het complete systeem (beschikbaarheid), of het compromitteren van de vertrouwelijkheid dan wel de integriteit van gevoelige informatie.
Een typisch voorbeeld is natuurlijk het CrowdStrike incident. Crowdstrike is een bedrijf die endpoint security levert (een geavanceerde virusscanner). Die maakt echter gebruik van de Windows kernel mode. "Normale applicaties" draaien nooit in kernel mode, die is alleen bedoeld voor drivers. Als een normale app crashed, dan crasht alleen DIE app. Als software in kernel mode draait crasht, dan crasht Windows als geheel (blue screen of death). Om een applicatie in kernel mode te kunnen laten draaien moet je een grondig certificeringstraject van Microsoft doorlopen hebben en de software uitgebreid zijn getest, hetgeen ook het geval was voor CrowdStrike. Maar de bug zat hem niet in de drivercode zelf maar in de virusdefinitie bestanden die continu worden aangevuld en dus niet het test traject hebben doorlopen van Microsoft.
Ander voorbeeld is een web framework die al jaren niet meer werd onderhouden en waarin een remote code execution vulnerability in blijkt te zitten. Middels een exploit die gebruik maakte van die RCE-gevoeligheid werd (geheel geautomatiseerd) via command line toegang op de linux node een shell script gedownload die vervolgens op zijn beurt weer software ging downloaden en uitvoeren. In dit geval was het Monero coin mining software. Totaal onbewust van wat er gaande was, reageerden wij hopeloos reactief op deze hack. Klanten klaagden over een trage responstijd van onze SaaS als gevolg van de zeer rekenintensieve software die grotendeels de processorcapaciteit overnam.
Dit voorbeeld illustreert het belang van volledige zicht hebben op de gevoeligheden die gebruikte 3rd party software zoals open source libraries met zich meebrengen. Middels het gebruik van Software Compositie Analyse en het tijdig patchen van libraries kan dit risico redelijk worden beheerst, al blijft er een restrisico op zero day gevoeligheden die of nog niet zijn ontdekt door specialisten, of wel bekend zijn maar nog niet zijn opgelost door de leverancier. In dat geval zouden mitigerende maatregelen een (tijdelijke) uitkomst kunnen zijn. Dit zijn vaak korte low-cost maatregelen die de oorzaak van de gevoeligheid weliswaar niet oplossen maar wel voorkomen dat deze geëxploiteerd kunnen worden. Een voorbeeld daarvan is het toevoegen van specifieke filtering regels aan een Web Application Firewall.
Bij het toepassen van security is het belangrijk om de juiste mate toe te passen. Het realiseren en onderhouden van security vraagt dikwijls veel tijd en geld. Er moet een gezonde afweging worden gedaan ook kijkende naar de risico's. Wat is de mate van gevoeligheid van de data waarmee wordt gewerkt? Dit zijn belangrijke overwegingen die aan het begin van de software lifecycle in de requirements fase moet worden genomen. Te weinig security maatregelen betekent dat je als organisatie onacceptabele risico's loopt. Teveel maatregelen anderzijds betekent ook weer verspilling van tijd en geld. Zodoende is security in feite risicomanagement. Bovendien zal het nooit mogelijk zijn om alle risico's af te dekken. Al heb je je security nog zo goed op orde, je houdt altijd een zogenaamd restrisico over, waarbij het dan wel belangrijk is dat dit een acceptabel risico is. Maar hoe bepaal je nu wat een acceptabel risico is? Belangrijk is daarbij om te beginnen die risico's in kaart te brengen. Vervolgens kan de mate van elk risico gekwantificeerd worden middels een systeem van punten tellen en daar vervolgens de volgende formule toe te passen:
Risk = Probability x Impact
Om concrete waarden te leveren voor Waarschijnlijkheid en Impact wordt dan vaak de Threat modeling methode toegepast. Treat modelling is een of meerdere teams sessies waarin deze risico's worden geïnventariseerd. Een veel gebruikt handvat is het zogenaamde STRIDE model voor het identificeren van mogelijke risico's op basis van bekende categorieën van bedreigingen, rekening houdend met de mate van gevoeligheid van de informatie. Dit model heeft zijn oorsprong binnen Microsoft en staat voor de volgende categorieën van bedreigingen:
Spoofing: identiteitsvervalsing
Tampering: vervalsing van informatie, waarbij dus de integriteit worden geschonden
Repudiation: verhullen, ontkennen of niet kunnen bewijzen van bepaalde acties zijn zijn gedaan
Information Disclosure: het vrijgeven van gevoelige informatie aan niet geautoriseerde personen
Denial of Service: platleggen of vertragen van informatie systemen
Elevation of privilege: iemand met een beperkte toegang kan ook informatie zien waar hij niet voor is geautoriseerd, of kan zichzelf een hogere rol toekennen zoals admin
Als er eenmaal een lijst van mogelijke bedreigingen uit is gekomen dan kan voor elk van die risico's een Waarschijnlijkheid en Impact score worden bepaald, om vervolgens binnen de juiste lifecycle fase passende maatregelen te treffen. Deze maatregelen kunnen uiteenlopen tot afdoende authenticatie, autorisatie, audit logging, scanning en de vele andere controles die mogelijk zijn, maar ook kosten en onderhoudslast met zich meebrengen.
Daarom is het ook belangrijk om naast het helder voor ogen te hebben wat de risico’s zijn die een organisatie loopt met zijn ICT diensten ook gepaste maatregelen te nemen. Ondanks dat zijn wij techneuten probleemoplossers zijn kan het soms juist beter zijn om hier niet voor te kiezen, alle factoren die hier van belang zijn in ogenschouw nemende, zoals:
Kosten vs baten: het probleem oplossen kan dusdanig kosten en tijdrovend zijn dat dit het risico zelf overstijgd
Business prioriteiten en eventuele reputatieschade
Juridische implicaties van de consequenties (datalekken, verlies, financiële schade enz.)
Gepaste maatregelen kunnen uiteindelijk bestaan uit de volgende RMTA stappen:
Remediate: het daadwerkelijk oplossen van de oorzaak
Mitigate: vermijden van het risico (zie het beschreven voorbeeld bij Single Point of Failure)
Transfer: het verhalen van het risico op andere partijen
Accept: accepteren van het risico (niks doen)
De eerste twee stappen behoeven geen nadere toelichting. Het derde punt kan uit verschillende maatregelen bestaan. Zo is het mogelijk om risico’s contractueel af te dekken door het expliciet te benoemen in een contract met eindgebruikers. Een andere mogelijkheid is een verzekering afsluiten tegen de financiële consequenties. Als laatste kan er ook voor worden gekozen om het risico te accepteren voor wat het is en helemaal niks te doen als de consequenties weliswaar vervelend maar aanvaardbaar zijn.
Dit principe bestaat kort gezegd uit het idee dat als je iets beveiligd (bijvoorbeeld middels authenticatie) dat je dit dan overal en op elk moment van toegang controleert, niet alleen na succesvol eenmaal te zijn ingelogd. Binnen OAuth2 gaat dit dan middels een token met een beperkte levensduur.
Een ander voorbeeld: een open en onbeveiligde endpoint die als troubleshoot log diende waar ook gevoelige persoonsgegevens in terug te lezen waren. Dit is een voorbeeld van waar goede intenties van software ontwikkelaars (traceability) onbedoeld onacceptabele risico’s introduceren als gevolg van de verkeerde gekozen oplossingsrichting.
Als een gebruiker zich belemmert voelt om een systeem te gebruiken dan gaan ze eromheen werken. Een goed voorbeeld hiervan zijn de complexe password eisen die veel diensten stellen. Er wordt nog steeds veel gebruikgemaakt van het principe dat een wachtwoord minimaal X tekens lang moet zijn, hoofdletters, kleine letters, cijfers en speciale tekens moeten bevatten. Dit voorkomt weliswaar dat het risico op bepaalde vormen van breaches wordt voorkomen (zoals credential stuffing) wanneer hier op de juiste manier mee wordt omgegaan. Maar wat er vaak gebeurt is dat mensen overal hetzelfde wachtwoord gaan gebruiken. De combinatie van overal unieke wachtwoorden gebruiken en tegelijk deze draconische password complexiteit eisen afdwingen, creëert voor gebruikers een onmogelijke situatie van de systemen die wij bouwen, tenzij ze allemaal gebruik gaan maken van een wachtwoordmanager. En dat is voor veel mensen een brug te ver..
Hackers maken gebruik van geavanceerde methoden om wachtwoorden te achterhalen, zoals credential stuffing waarbij bestaande databases van gelekte wachtwoorden worden gebruikt. Ook als deze wachtwoorden licht afwijken van elkaar (door bijvoorbeeld een cijfer toe te voegen) is die credential stuffing software nog zo slim om hier rekening mee te houden.
Waar het dus eigenlijk op neerkomt is dat enkel vertrouwen op wachtwoorden voor authenticatie niet meer voldoende bescherming biedt tegen lekken, hetgeen multi factor authenticatie noodzakelijk heeft gemaakt.
Een goede ontwikkeling hierin is daarom Passwordless toegang, wat gebruik maakt van een combinatie van public-key cryptografie met biometrie. Dit maakt authenticatie zowel aanzienlijk veiliger dan gebruik van wachtwoorden als zeer laagdrempelig voor gebruikers (enkel identificatie met bijvoorbeeld een vingerafdruk volstaat).
Maar juist die laagdrempeligheid brengt weer met zich mee dat gebruikers het (ten onrechte) gaan wantrouwen. Verder verloopt de adoptie van Passwordless moeizaam, deels omdat mensen over jaren heen zo vertrouwd zijn geraakt met wachtwoorden. Anderzijds omdat het dus zo eenvoudig voelt dat mensen denken dat het niet veilig is, maar dat is het dus wel.
Als er fouten optreden (bijvoorbeeld een exceptie) dan is het zaak dat daarbij de vertrouwelijkheid, integriteit en beschikbaarheid van de data niet wordt ondermijnd. Dus als een error wordt weergeven in een gebruikersinterface of een log dan dient deze geen gevoelige data weer te geven of voor hackers interessante info te onthullen over de interne werking van een systeem.
Een voorbeeld: een HTTP foutcode die niet goed wordt afgevangen door de frontend resulteert in het tonen van een applicatie server info pagina waar ook de versie van de server software op staat, of de versie van het linux operating systeem waar de software op draait. Voor kwaadwillenden is dit gouden informatie want vervolgens kunnen ze gaan zoeken in de lijst van gevoeligheden die zijn gepubliceerd voor de specifieke versie. Als de server software vervolgens ook niet tijdig is gepatcht naar de nieuwere versies dan is het risico op kritieke gevoeligheden zoals remote code execution nog groter.
Hierbij geldt het principe dat je meerdere lagen van beveiliging kan inbouwen zodat niet je gehele veiligheid afhangt van een enkele maatregel. Een voorbeeld is de combinatie van encryptie (voor data in transit) en authenticatie.
Wat ik hier als voorbeeld van een valkuil kan geven is dat de toegang tot bepaalde API's in de cloud geheel afhankelijk is van een enkele authenticatie controle, bijvoorbeeld toegang tot de private cloud zelf. Op de endpoints zelf zit vervolgens geen authenticatie. In zo'n geval is het goed om toegang tot de API endpoints te koppelen aan dezelfde authenticatie die door de frontend wordt gebruikt (vaak OAuth). Het probleem hier is niet dat de toegangscontrole van de private cloud niet afdoende is (integendeel), maar dat enkel toegang tot de private cloud afdoende is. En wie heeft er allemaal toegang tot die private cloud binnen (en buiten?) de organisatie? Zijn dit medewerkers of externen die bevoegd zijn tot het inzien van de gevoelige data die opvraagbaar is via deze endpoints?
Open design betekent dat je beter uit kunt gaan van security-algoritmen die open source en daarmee "review" proof zijn als die wordt onderhouden door een grote community met een gedegen reputatie. Dit in tegenstelling tot "proprietary" implementaties die "geheim" moeten blijven, wat eigenlijk neerkomt op de security anti-pattern security by obscurity. Een goed voorbeeld van open design frameworks is Spring Security. Hetgeen geheim moet blijven uitgaande van open design is zoiets als een sleutel, en niet het algoritme, het ontwerp of de code waar de security op is gebaseerd. Dit klinkt een beetje als een open deur, maar toch zul je dit nog wel eens tegenkomen. Eigenlijk zijn de meeste niet standaard oplossingen gebaseerd op security-by-obscurity. Het feit dat jij er als ontwikkelaar goed over hebt nagedacht en het uitgebreid is getest kan het per definitie niet op tegen bewezen technologie die dagelijks door gespecialiseerde security onderzoekers wordt getest op gevoeligheden.
Ik heb hier natuurlijk maar een tipje van de sluier opgelicht van wat belangrijke basisprincipes zijn in de software security, maar ik hoop jullie hiermee toch te hebben geïnspireerd om je er verder in te verdiepen. Met gedegen kennis van securityprincipes in het achterhoofd in elke fase van een project ontwikkel je een intuïtie om hopelijk dit soort problemen te voorkomen.
Geen van de genoemde voorbeelden hebben betrekking op situaties die ik beroepsmatig heb ervaren in de afgelopen 5 jaar. Een aantal voorbeelden dateren van voor die tijd en zijn destijds snel opgelost zonder dat daarbij significante schade is ontstaan.