Secure by Design - Hoofdstuk 8

Ingediend door Dirk Hornstra op 30-aug-2020 23:15

In januari zijn we gestart met hoofdstuk 1 (link), in februari met hoofdstuk 2 (link), in april met hoofdstuk 3 (link), in mei met hoofdstuk 4 (link), in juni met hoofdstuk 5 (link), in juli met hoofdstuk 6 (link), in augustus hoofdstuk 7 (link) en nu wordt het tijd voor het 8e hoofdstuk.

De Timo-samenvatting

Op dit moment zorgt onze build-straat (Jenkins) dat als code niet gecompileerd kan worden, deze ook niet gedeployed wordt. Dit hoofdstuk zegt eigenlijk dat je testen aan je projecten moet toevoegen en deze ook in je build-straat zou moeten meenemen. Vaak zijn veel testen functioneel, maar er zouden ook meer security-gerelateerde testen gemaakt moeten worden. Test je code op geldige input, input die de grenswaarden raakt, ongeldige input en "extreem ongeldige input", gooi maar eens 20.000 tekens in een invoerveld en kijk wat er gebeurt. Als je features gebruikt (delen van code actief maken voor nieuwe functionaliteit) zorg dat je met testen kunt controleren dat code niet ongewenst op productie actief wordt. Zorg dat je test met load op je applicatie, zodat je weet tot hoeveel gebruikers deze nog acceptabel werkt. Controleer je configuratie (geautomatiseerd) en ook de default-instellingen van externe bibliotheken. Mooi voorbeeld hiervan is AutoMapper. Gebruiken we als ORM in onze applicaties, maar met verschillende updates is functionaliteit anders geworden of volledig weggevallen. Met een goed test-plan moeten we van tevoren kunnen controleren wat een update voor impact heeft op onze code.

De titel van hoofdstuk 8 is "Leveraging your delivery pipeline for security".

De meeste developers zijn het er over eens dat testen een integraal deel van het ontwikkelproces is. Door die testen tijdens het ontwikkelen uit te voeren, zou je (als het goed is) een flink stuk "bug-fixing" na afronden van het project moeten kunnen voorkomen. Test-driven-development en behaviour-driven-development zijn ontwikkeld om tijdens het bouwen duizenden tests uit te kunnen voeren. Maar vaak zijn dit functionele tests en niet security-gerelateerde tests. Bij elke commit een penetratie-test uitvoeren is overkill, maar iets meer aandacht hieraan geven is niet verkeerd. Dit hoofdstuk gaat ons daar handvaten voor geven.

Een delivery-pipeline gebruiken

De "delivery pipeline" is het proces waarmee jouw code op de productie- (of acceptatie-omgeving) geplaatst wordt. De flow zoals die in het boek beschreven wordt is: code inchecken in GIT, unit tests, application tests, integration tests, system tests en availability tests uitvoeren. En als die allemaal groen licht geven, dan deployen naar de omgeving.

Beveilig je ontwerp met behulp van unit tests

Bij unit testen controleer je vaak wat de code zou moeten doen. Maar je moet ook controleren wat de code niet zou moeten doen. Het voorbeeld wordt gegeven van een telefoonnummer die in een string-variabele opgeslagen wordt. Daar kun je nummers in zetten, maar natuurlijk ook andere tekens. Het boek geeft 4 testmethodes:

  • normal input testing, controle of geldige waarden ook daadwerkelijk geaccepteerd worden.
  • boundary input testing, beperkingen die hier vaak voor gelden zijn lengte, grootte en aantal, maar kan ook andere regels bevatten (bijvoorbeeld mag niet negatief zijn, maar -1 is wel een geldige optie).
  • invalid input testing, controle dat de code niet crasht op ongeldige input (leeg, null, vreemde karakters).
  • extreme input testing, controle dat de code niet crasht op extreem ongeldige input (gooi er 40 miljoen karakters in).
     

Met het boek gaan we een voorbeeld case bekijken. Het gaat hier om een ziekenhuis, artsen en verplegend personeel sturen via de mail informatie heen en weer over patiënten. Deze correspondentie mag niet buiten het ziekenhuisdomein komen (niet naar externe mailadressen).

De domein regels voor het ziekenhuis

We gaan de regels opstellen voor de mail-adressen. Als je iets met mailadressen zou doen, zou je RFC 5322 erbij pakken, want deze bevat de regels voor e-mailadressen. Bij het ziekenhuis gelden echter beperkingen. Omdat er oude systemen draaien, die niet met "vreemde tekens" kunnen werken, mogen de mailadressen alleen alfanumerieke tekens bevatten, cijfers en punten. Totale lengte maximaal 77 tekens, het domein moet hospital.com zijn.
Het boek levert nog een lijstje met regels. In eerste instantie zou je de mailadressen in een "string-variabele" zetten, maar met deze restricties is het duidelijk, we maken een eigen domain primitive aan, EmailAddress.

Testen van normaal gedrag

We zien een stukje JUnit5 code, een unit-test waarbij geldige e-mailadressen aangeleverd worden en de test deze accepteert. Hier is het een vast lijstje, het boek raadt aan om een soort "geparameteriseerde test" te maken, zodat je een lijst kunt aanleveren en de testende code hiervoor niet hoeft aan te passen.

Testen van de grenswaardes

In een eerder hoofdstuk is de winkelwagen besproken, waarbij deze niet meer wijzigbaar mag zijn nadat je door de checkout procedure gegaan bent. Dit voorbeeld met e-mail is minder complex, je zit met beperkingen op tekens, lengtes.
We zien een tabel met "Accept" en "Reject", waarbij dus aan de ene kant de grenswaardes zitten die OK zijn, aan de andere kant de waardes die NIET OK zijn.

Bij deze test ontstaat de bevinding dat de regex die gebruikt wordt voor validatie van het adres te zwak is, er zitten geen lengte-beperkingen in. Er wordt nog even terug verwezen naar hoofdstuk 4, dat je eigenlijk eerst een lexicale controle zou moeten uitvoeren, voordat je een regex op de input laat valideren. Omdat de controles op lengte nu toegevoegd zijn is dat in dit testvoorbeeld niet nodig.

Als deze testen werken nemen veel developers een bak koffie en zeggen "klaar!". Maar je bent pas op de helft...

Testen met ongeldige input

Vaak wordt met null, lege strings, vreemde karakters getest om te zien wat er gebeurt. In het voorbeeld van het boek blijkt dat er gecontroleerd wordt op een .length(), maar op het moment dat de invoer een NULL-waarde is, crasht die code. In de code voegt het boek dus eerst een IsNull-controle toe.

Behalve het valideren op dit type input noemt het boek ook het toevoegen van SQL (waardoor mogelijk SQL injection toegepast kan worden) en XSS (dus dat er bijvoorbeeld <script>alert('test')..</script> in toegevoegd kan worden). De tekens zouden geldig kunnen zijn, maar bij latere acties in de database (of bij het tonen op het scherm) problemen kunnen veroorzaken.

Testen met extreme waardes

Door het injecteren van heel! veel! input kun je zorgen dat de performance van de applicatie afneemt, memory leaks veroorzaken of ander ongewenst gedrag. Je kunt wel bepaalde checks hebben, maar als je die check niet goed geïmplementeerd hebt, kan dat voor problemen zorgen. Ook hier weer de verwijzing naar hoofdstuk 4 waarbij we toen al zeiden dat je eerst moet controleren op lengte voordat je de ingewikkelde regexen hun werk laat doen. Als er 4 miljoen tekens binnen komen, kun je die meteen afwijzen.

Feature toggles, nieuwe functionaliteit "per ongeluk" online

Met bepaalde "toggles" kun je voor een klant (of collega) op de site de aangepaste productpagina laten zien. Of de nieuwe winkelwagen die gebruikt gaat worden. Maar die "toggles" kunnen ook voor security-problemen zorgen. Het boek komt met het volgende voorbeeld;

Het voorbeeld gaat over een ploeg programmeurs die hun zaakjes goed voor elkaar lijken te hebben. De groep ontwikkelt een API, die door de testen heen gaat en dan past live gezet wordt. Omdat er een nieuwe feature moest komen werd er een toggle toegevoegd, werd gebruikt bij lokale tests op de ontwikkel-pc van de developer en op de Continuous Integration server of bij het uitvoeren van testscenario's op een test-omgeving. De nieuwe functionaliteit was beschikbaar via een publieke API. Er moesten nieuwe toegangsregels geïmplementeerd worden, maar dat werd door een ander team gedaan. Die toggles worden bijgehouden via de .config-file, en als er veel features zijn, zijn er veel toggles. Zoals te verwachten was had een ontwikkelaar per ongeluk deze toggle aangezet en gedeployed naar productie. Omdat de authenticatie nog gebouwd moest worden, was deze functionaliteit publiekelijk beschikbaar. Het team kwam er zelf op tijd achter.

In het boek wordt nog even beschreven wat "feature toggling" is. We hebben dat hierboven al toegelicht.

De toggles onder controle houden

Het boek raadt aan om voor elke toggle die je toevoegt ook een test te bouwen die controleert of de toggle werkt zoals bedoeld is.
In het voorbeeld had afgedwongen kunnen worden dat deze feature nooit naar productie overgezet kan/mag worden. De test was gefaald en hierdoor was het deployment niet uitgevoerd.

We krijgen een lijstje met mogelijke toggle-configuraties:

  • remove funcionality in public API: controle door als deze verwijderd is een 404 in API call te geven, bericht negeren/weggooien of verbinding op een socket weigeren.
  • replace existing functionality: controle door de nieuwe actie uit te voeren. Op basis van het resultaat op neit bestaande UI elementen kan gecontroleerd worden dat het nieuwe gedrag niet gezien wordt.
  • nieuwe authenticatie/authorisatie: alleen op oude manier inloggen werkt, nieuwe manier werkt niet.
  • wisselend gedrag: als feature A aan staat, moet B niet uitgevoerd/beschikbaar zijn en vice versa.


Omgaan met gecombineerde complexiteit

Als je meerdere toggles hebt zou je eigenlijk alle combinaties moeten testen. De ene wijziging kan via-via invloed hebben op een andere wijziging.

Auditing toepassen op toggles

Het beschikbaar maken van de toggles, dat moet op een veilige manier gebeuren. Omdat de applicatie qua gedrag verandert (je kunt ineens zelf een korting-bedrag invullen in je winkelwagen!) moet het aanpassen beschermd zijn dat het alleen door geautoriseerde toegang uit te voeren is. Ook zou het wijzigen van een toggle-state gelogd moeten worden, zodat na de tijd te controleren is wie-wat-en-wanneer aan of uitgezet heeft. Toggles worden steeds meer toegepast in software-ontwikkelland, het is een mooie feature, maar kent dus de nodige haken en ogen!

Geautomatiseerde veiligheidstests

Niet alle testen zijn te automatiseren. Het boek wil hier toch een opzetje voor geven, zodat je een geautomatiseerde mini-pen test krijgt.
Het boek geeft aan:

  • handel je mislukte login-pogingen goed af? schrijf er een test voor.
  • heeft je online discussie forum goede bescherming tegen XSS? schrijf een test die bogus-data toevoegt.

 

We hebben 2 type security-tests:

  • Focus op de applicatie: controle op HTTP headers en input-validatie
  • Focus op de infrastructuur: controle op open poorten en de rechten van de lopende processen

 

OWASP tools: https://www.owasp.org/index.php/Category:Vulnerability_Scanning_Tools

OWASP dependency check: https://www.owasp.org/index.php/OWASP_Dependency_Check

Infrastructure as code

Webserver, firewalls, kunnen op basis van scripts e.d. opgezet worden. Dit heeft meer betrekking op de zaken die Vevida doet en is voor ons niet van belang.

Het testen van beschikbaarheid

Als je zelf test, werkt de site "als een tierelier". Maar dan gaat de site live, zitten er 50 bezoekers op en wordt hij super-traag. WTF. Het boek geeft aan dat je dit van tevoren moet testen, eigenlijk als een soor DoS attack om te bepalen wat je "headroom" is, met hoeveel verbindingen/bezoekers draait je site nog goed?

Je kijkt daarbij naar geheugengebruik, CPU, reactietijd. Het is ook bedoeld om de zwakke plekken in je applicatie op te sporen. Het boek geeft het voorbeeld van Bees with Machine Guns (vette naam!) voor een Amazon EC2 platform, waarmee je 100.000 requests door 8 instanties kunt laten afvuren.

Het misbruiken van domain rules

Het boek geeft aan dat dit type aanval een aanval is waarbij de regels die voor de applicatie gelden in acht worden genomen, maar de bedoeling erachter minder goed is. In dit geval gaat het om een hotel die een niet-goed-geld-terug regeling heeft. In dit voorbeeld heeft de eigenaar van het hotel bepaald dat als voor 16.00 uur geannuleerd wordt, je het volledige bedrag terug krijgt. Een script-kiddie kan in dit geval alle kamers boeken en weer terug laten storten: het hotel heeft een dag omzet gemist. Oeps.

Het schijnt dat dit in het echt gebeurd is, niet bij hotels, maar bij Lyft/Uber, waarbij taxi's geboekt worden en dan geannuleerd.

Het valideren van configuratie

Het boek geeft aan dat bepaalde zaken in code "extern" zijn. Via nuget haal je externe bibliotheken op, bijvoorbeeld Automapper voor connectie met de database en objecten in je code, Elmah voor het loggen van problemen, etc.

De mogelijke oorzaken van foutieve configuraties zijn onbewust aangebrachte aanpassingen, met opzet aangebrachte wijzigingen, of verkeerd begrepen configuraties.

Onbewust aangebrachte aanpassingen

Per ongeluk een regel verwijderd, type-fout in een parameter.
Er zou een test moeten zijn die deze zaken controleert (bijvoorbeeld dat errors op productie altijd verborgen zijn).

Bewust aangebrachte wijzigingen

In dit geval pas je code bewust aan omdat je een nieuwe feature hebt. Wat je over het hoofd ziet is dat een andere developer de code juist zo ingesteld had naar aanleiding van een penetratie-test van een ander deel van de code. Door jouw aanpassing is dat deel nu weer "vulnerable". Dit is een moeilijk scenario, want in bestaande code zijn niet altijd bestaande tests gebouwd, anders had je deze wijziging daarmee kunnen valideren.

Niet begrepen configuratie

Het boek noemt hierbij API's die niet duidelijk zijn. Wat ik hieruit opmaak is bijvoorbeeld een check als "if (!a != b) ...", een "negating statement". Als je iets aanpast, zorg dat je ook testen bouwt.

Schrijf geautomatiseerde tests

Het boek komt met een lijstje:

  • Web containers: HTTP headers, CSRF tokens, Output encoding
  • Network communication: Transport Layer Security (HTTPS and so on)
  • Data parsing: Behavior of data parsers (such as XML and JSON parsers)
  • Authentication mechanisms: Authentication on/off, Integration settings (for example, for CAS and LDAP)


Bij TRES worden al flink wat zaken via onze Zabbix-monitoring in de gaten gehouden.

Ken je standaardwaardes en controleer ze

Bij sommige externe bibliotheken die je gebruikt, wordt een bepaalde standaard configuratie gebruikt als je zelf niets instelt, "implicit behaviour". Als voorbeeld wordt een REST service genoemd, als alleen GET als protocol gebruikt wordt, zorg dan dat POST/PUT, etc. geweigerd wordt. Dit is met een geautomatiseerde test te controleren. TRACE is ook zo'n methode, die veel gebruikt wordt voor cross-site tracing (XST).