Security by design bij informatiesystemen

IT-ROCKSTAR CHRISTIAAN NIEUWLAAT

Informatiebeveiliging is altijd al belangrijk geweest, maar wordt met de dag belangrijker. Steeds meer informatie wordt op het internet opgeslagen, en loopt daarmee risico. Gelukkig komen er steeds meer mogelijkheden voor adequate beveiliging van informatie en informatiesystemen. Deze maken het voor onbevoegden steeds moeilijker om bij de informatie te komen.

Maar… achteraf beveiliging toevoegen is nooit zo effectief als het op voorhand al rekening houden met beveiliging. Dit laatste noemt men “Security by design”.

In dit document geef ik wat achtergrondinformatie over informatiebeveiliging en een korte inkijk hoe de security-by-design filosofie werkt. Samengevat is het antwoord op de vraag “Waarom zou ik security by design toepassen?” “Als je beveiliging vanaf het begin meeneemt, dan kom je aan het eind goedkoper uit”.

Om dit toe te lichten wil ik een vergelijking maken met een voordeur.

Het uitbreiden van de veiligheid van een standaard voordeur, zo een met alleen 1 enkel slot in het midden, is mogelijk door het toevoegen van extra inboor-sloten, een aan de bovenkant en een aan de onderkant. Dan is de deur over de gehele lengte te sluiten. Hierdoor is het doel, extra beveiliging, bereikt, maar…

  1. Het achteraf aanbrengen van de sloten is kostelijk 
  2. Minder gebruiksvriendelijk, want je moet nu in plaats van 1 slot, 3 sloten bedienen
  3. Lastig om in te bouwen, aanpassingen aan kozijn en deur nodig, moeten precies goed uitgelijnd zijn
  4. Minder fraai om te zien, je ziet duidelijk de 2 extra sloten ingebouwd zitten.

Had je nu op voorhand gekozen om geen standaard-deur te plaatsen, maar een deur met drie-punts-sluiting, dan had je vooraf gedacht aan de extra beveiliging en had je tegen een geringe meerprijs direct een betere oplossing gehad.

Vanuit het oogpunt van security-by-design zijn er meerdere niveaus waarop beveiligingsmaatregelen kunnen worden ingebouwd om een informatiesysteem te beveiligen, te weten:

  • Platform-/netwerk architectuur
  • Software / deployment
  • Communicatie met “de buitenwereld”

Hieronder heb ik een niet uitputtende lijst met “drie-punts-sluitingen” welke je software veiliger kunnen maken uitgewerkt.

Platform- / Netwerk architectuur

Scheiding publieke en private onderdelen

Bij het opzetten van een omgeving, is het belangrijk om van het begin af aan rekening te houden met welke services/onderdelen publiekelijk benaderbaar moeten zijn en welke vooral niet. Dit kan je onder meer doen door op netwerkniveau je netwerk op te delen in een publiek en een (of meerdere) private subnet(ten) en de verschillende services op de verschillende subnetten te plaatsen.

Gebruik een VPC/VNet als je gebruik maakt van de cloud

Door het gebruik van een VPC / Virtueel netwerk kun je een hogere mate van beveiliging opzetten. Binnen je VPC kan je een compleet eigen virtuele omgeving opzetten die alleen in dit VPC leeft. Van buiten het VPC weet niemand wat er waar staat, en je moet zelf een ingang naar je VPC creëren, naar je public facing service(s).

Bastion

Het is verstandig om voor het beheer van je virtuele omgevingen een zogenaamde bastion server in te richten in je public subnet. Deze server is het enige entrypoint waarlangs je je virtuele omgeving kunt benaderen. (bijvoorbeeld de enige server waar de SSH poort open gezet is voor verkeer van buitenaf). Voor extra beveiliging is het raadzaam om:

  1. alleen de IP (-range(s)) van de machine(s) van de beheerders toegang tot de open poorten te geven via de firewall.
  2. Te werken met signed ssh keys i.p.v. wachtwoorden

Tevens kun je de bastion server gebruiken als tunnel-host voor bijvoorbeeld de database management tooling. Deze lopen dan via een SSH-tunnel over de bastion-server naar een host in je virtuele netwerk.

Communicatie tussen virtuele machines

Zorg bij de opbouw van je netwerk ervoor dat de communicatie van de machines onderling waar mogelijk versleuteld is (bijvoorbeeld door TLS). Mocht er onverhoopt een breach plaatsvinden dan is het sniffen van netwerkverkeer niet voldoende om eenvoudig informatie te onderscheppen.

“Hardenen” van je docker images

In de huidige wereld van virtuele infra en containers is het van primordiaal belang dat de container images veilig zijn. Je kan namelijk je software helemaal beveiligen, maar als bijvoorbeeld de base image een lek bevat dan hebben alle maatregelen die je zelf neemt minder nut. Je zou een tool als Chef Inspec1 in combinatie met een goede dev-sec baseline2 kunnen gebruiken om voor het publiceren van je container image te controleren of de image veilig genoeg is. Eventuele stappen die je nog kunt uitvoeren worden dan getoond.

  1. Chef Inspec:  https://inspec.io
  2. Dev-Sec baseline:
    1. Linux: https://github.com/dev-sec/linux-baseline
    2. Windows: https://github.com/dev-sec/windows-baseline

Least privileged access

Bij het toekennen van rechten aan gebruikers dient er rekening mee gehouden te worden dat gebruikers niet meer rechten krijgen dan dat deze nodig hebben om de geplande werkzaamheden te kunnen doen. Bij voorkeur zijn er geen onnodige gebruikers aangemaakt, eventuele “meegeleverde” gebruikersaccounts kunnen het best verwijderd worden als deze niet gebruikt worden.

Software / deployment

OWASP top 10

De Open Web Application Security Project (OWASP) Foundation houdt een top 10 bij van meest voorkomende beveiligingsrisico’s bij web applicaties bij. Per item wordt uitgelegd wat deze inhoud en eventuele mogelijke oplossingen. Deze top 10 is zoals de naam aangeeft toegespitst op web applicaties, maar een aantal onderwerpen spelen ook bij niet web-gebaseerde applicaties.

Deze top 10 lijst is te vinden op https://owasp.org/www-project-top-ten/ en het is raadzaam om bij de ontwikkeling van een applicatie deze punten in het oog te houden.

Niet doorgeven technische foutmeldingen

Een veel gemaakte fout is dat er bij het optreden van een fout in een applicatie deze rechtstreeks naar het scherm wordt afgedrukt. Het risico hierbij is dat er (bijna altijd) informatie over de interne werking van de applicatie (variabele-namen, stacktraces, etc) in de foutmelding staat waardoor eventuele malafide gebruikers kennis kunnen krijgen over de interne applicatie structuur.

Het is beter om de foutmelding netjes af te vangen, en een gebruikersvriendelijke melding aan de eindgebruiker te geven (waar dus géén technische informatie in staat), en de echte foutmelding in een logbestand op te nemen.

Je kunt dit vergelijken met het doorgeefluik van de keuken bij een restaurant. Als de maaltijden steeds goed door het luik worden verstrekt om opgediend te worden, hoef je niet te weten hoe het er in de keuken aan toe gaat.

Valideer en schoon gebruikersinvoer

In beginsel is gebruikersinvoer niet te vertrouwen. Dit is misschien een heel beladen stelling, maar zonder validatie / opschonen van deze invoer, zou het zomaar mogelijk kunnen  zijn om een zodanige invoer in de applicatie in te sturen, dat de applicatie een foutsituatie creëert en ongewenst gedrag gaat vertonen. Bijvoorbeeld door te crashen, maar in ernstiger geval dat er zogenaamde remote code execution plaatsvindt (met alle gevolgen van dien)

Zorg voor rate limiting / hammering beveiliging

Als er teveel requests (vanuit eenzelfde bron) in korte tijd naar een applicatie worden gedaan, hangt deze zichzelf mogelijk op door overmatig gebruik van resources. Om dit tegen te gaan kan de applicatie de aantallen requests binnen een bepaalde tijdsperiode begrenzen en de surplus verbindingen direct verbreken.

Maak gebruik van SAST/DAST om eventuele zwakheden te ontdekken

Ongeveer 90% van de beveiligingsincidenten komt voort uit “reeds bekende” fouten welke door aanvallers worden uitgebuit. Om ervoor te zorgen dat deze “bekende” fouten niet in de software worden gemaakt, kan er gebruikt worden van Static Application Security Testing (SAST) en Dynamic Application Security Testing (DAST) tools.

SAST wordt ook wel “white-box” testen genoemd, en laat ontwikkelaars vroegtijdig beveiligingszwakheden in de code ontdekken. Tevens zorgt een SAST ook voor het melden van afwijkingen op coding-standards. Dit doet ie zonder de code te hoeven uitvoeren.

DAST, oftewel “black-box” testen, werkt daarentegen op een draaiende applicatie, door bijvoorbeeld bekende misbruikmethoden als malafide data injectie op de applicatie af te vuren en te kijken hoe de applicatie reageert. Hierdoor zijn bijvoorbeeld zwakheden als SQL Injection te testen.

Omdat DAST net iets andere dingen test als SAST is het aan te raden om beide te gebruiken.

Integreer RASP (Runtime Application Self Protection)

RASP is een mechanisme wat op basis van metrieken en heuristiek verdachte patronen kan herkennen. De applicatie wordt hierdoor “slim” en kan zelfstandig en eventuele acties ondernemen op bepaalde patronen. Het grote nadeel van een dergelijk systeem integreren is dat je er van begin af aan mee moet beginnen en je aan bepaalde richtlijnen vast zit (om het systeem te kunnen laten werken). Een voorbeeld van zo’n RASP is de OWASP AppSensor (https://appsensor.org)

Neem penetratietesten op in je test proces

Stel… er is ondanks de beste bedoelingen toch een beveiligingsincident opgetreden in je applicatie. Neem dan de aanval als penetratietest op in je testsuite. Hierdoor kun je feitelijk vaststellen dat deze aanval niet per ongeluk terug mogelijk is door eventuele aanpassingen. Een tool welke je hierbij goed kunt gebruiken is Gauntlt (http://gauntlt.org/).

Waak voor fouten door “niet-beschikbare” afhankelijkheden

Zodra een informatiesysteem bestaat uit meerdere componenten met onderlinge afhankelijkheden, is het belangrijk dat het systeem niet onderuit gaat als een van de afhankelijkheden niet beschikbaar is. Dit geldt voornamelijk voor distributed systemen of microservice systemen. Zorg bij dit soort systemen voor een fallback-mechanisme waar mogelijk zodat het systeem op een later tijdstip het verzoek kan afmaken. Mocht het zo zijn dat dit écht niet mogelijk is, dan zal het systeem dit netjes moeten opvangen door pro-actief een nette foutafhandeling te doen, en daarbij ervoor te zorgen dat er geen neveneffecten hebben plaatsgevonden. Dit kan bijvoorbeeld door middel van een circuit-breaker design pattern te implementeren, waardoor opvolgende verzoeken netjes kunnen falen, zodra een afhankelijkheid onbeschikbaar raakt.

Communicatie met de buitenwereld

Wees ruim in wat je accepteert

Als je API een numerieke waarde bevat, dient deze het ook te accepteren als het aangeleverd wordt als tekenreeks i.p.v. een foutmelding te geven dat het type verkeerd is. Ondanks dat strongly typing juist als best-practice aangemerkt is, geeft het melden dat je een bepaald type verwacht informatie die mogelijk misbruikt kan worden. Door zelf de invoer naar het juiste type te converteren (maar wel te loggen dat dit gedaan is voor dit request) houd je het wel voor jezelf inzichtelijk, maar geef je geen te misbruiken informatie vrij.

Wees conservatief in wat je verstuurt

Probeer de informatie die je teruggeeft zo beknopt mogelijk te houden. Je wilt niet meer informatie vrijgeven dan noodzakelijk om het request te beantwoorden. Als iemand bijvoorbeeld vraagt om de NAW gegevens van iemand, dan hoef je niet het BSN en/of geboortedatum mee terug te geven. Dit zou namelijk meer informatie dan nodig teruggeven, die wel gevoelig kunnen zijn. (NB! dit is sowieso een eis van de GDPR/AVG als het om persoonlijk identificeerbare informatie gaat)

Gebruik de laatste versies van de beveiligingsprotocollen

Gebruik sowieso een versleutelde verbinding als er gevoelige data over de lijn gaat. Zorg ervoor dat je dan wel gebruik maakt van de laatste versies van de beveiligingsprotocollen, oudere versies van SSL / TLS zijn niet meer veilig anno 2020