Security by design: integriteit van data
Geschreven door Rob Dekkers
Data-integriteit betreft de juistheid en consistentie van de opgeslagen gegevens. De gegevens ontstaan via de daarvoor ingerichte processen of worden door geautoriseerde en bekwame gebruikers vastgelegd. Integriteit kan worden aangetast door fouten van gebruikers (soms wat minder respectvol aangeduid met PEBCAC, Problem Exists Between Chair And Computer), fouten in de verwerkende systemen of moedwillige acties. We gaan ervan uit dat de softwaresystemen voldoende zijn getest, zodat consistentie wordt gewaarborgd. Toch zijn er niet zo evidente aandachtspunten voor ontwikkelaars die we hier ook zullen noemen. Gebruikers moeten door de software worden ondersteund om het maken van fouten zoveel mogelijk te reduceren. En moedwillige acties om het systeem te verstoren willen we natuurlijk onmogelijk maken. Kwetsbaarheden in de bewaking van de integriteit staat in de OWASP-lijst op nummer 8 (Software and Data Integrity Failures).
Een voorbeeld van integriteitschending
Een onschuldig voorbeeld van inbreuk op de integriteit kan worden gevonden in de Developer portal (sprintr, een Mendix app). Bij stories kunnen labels worden vastgelegd. De kleur van een label kan worden geconfigureerd uit een vaste selectie van kleurnamen. Hieronder is een voorbeeld gegeven.
Via de client API is in entiteit PCP.Label het attribuut BackgroundColor vervangen door #000000 (black) en TextColor vervangen door #ffffff (white). De consistentie in data is daarna verstoord; het “Yellow” label ziet er dan namelijk als volgt uit:
Dit probleem kan op verschillende manieren worden opgelost:
- De autorisatie om de attributen BackgroundColor en TextColor aan te passen wordt verwijderd. Deze waarden moeten dan in een microflow zonder Apply Entity Access worden gewijzigd op basis van de waarde van de Label Color.
- Bij het opslaan van het label (event handler) bepaalt de Label Color altijd de twee kleurattributen.
- Bij het opslaan wordt de inconsistentie geweigerd onder vermelding van de fout.
- De gebruiksdefinitie van het attribuut kleur wordt aangepast als alleen een preselect van kleuren die later naar eigen wens kunnen worden aangepast (flauw).
Inputvalidatie
Een belangrijke maatregel tegen integriteitsinbreuken is beperkte autorisatie, zoals we in een eerdere blog hebben besproken. Een andere belangrijke maatregel is inputvalidatie. Inputvalidatie kan ervoor zorgen dat fouten door gebruikers minder makkelijk kunnen worden gemaakt en maakt het kwaadwillende gebruikers ook een stuk moeilijker. Omdat ook via de client API kan worden gemuteerd op objecten, is het niet voldoende om validaties alleen in schermen of met nanoflows te implementeren. De waarden dienen dan altijd ook nog op de server te worden gevalideerd in event handlers of microflows (waarbij het altijd een afweging is tussen risico en ontwikkelkosten).
- Controleer of de waarde van een attribuut voldoet aan de mogelijke verzameling waarden, zoals reeksen en formaten (bijvoorbeeld met reguliere expressies).
- Controleer of de waarde van een attribuut een toegestane wijziging is ten opzicht van de vorige waarde zoals bijvoorbeeld maximaal 10% stijging.
- Controleer op plausibiliteit van de waarden van attributen (bijvoorbeeld bij het opgeven van meterstanden van elektraverbruik).
- Controleer op de logische combinatie van attribuutwaarden binnen een object of tussen objecten.
Een algemene regel is om bij inputvalidatie altijd uit te gaan van wat toegestaan is en daarop te controleren en niet om te controleren op wat niet is toegestaan. Dit is een logisch gevolg van de regel om de gebruiker zo min mogelijk rechten te geven (minimize the attack surface).
Onzorgvuldigheid in inputvalidatie staat in de CWE-lijst 2022 op nummer 4 (Improper Input Validation).
Multi-threading
Ook in zelf ontwikkelde programmatuur kan onbedoeld integriteitschending plaats vinden, zelfs na heel goed getest te hebben. Hoewel de eigen programmatuur binnen de trust boundary valt, willen we toch aandacht schenken aan multi-threading, omdat problemen hiermee moeilijk zijn te herkennen door het niet-deterministische karakter van de symptomen.
Een Mendix app verwerkt transacties van verschillende gebruikers en voert scheduled events uit. Bij het ontwerp van de app dient rekening gehouden te worden met eventuele integriteitsproblemen die als oorzaak hebben dat verschillende transacties tegelijkertijd op dezelfde data werken. Een veel voorkomende probleem dat moeilijk reproduceerbaar is, is het Read-Modify-Write probleem.
Het Read-Modify-Write-probleem ontstaat als een microflow een leesactie op de database uitvoert, op basis van de gelezen data conclusies trekt, mutaties doorvoert en als laatste de transactie in de database commit. Indien een tweede microflow ook een Read-Modify-Write-patroon op dezelfde data volgt, dan kan het zijn dat de tweede microflow conclusies trekt die op het moment van lezen juist zijn, maar op het moment van de commit van de eerste microflow niet meer juist zouden zijn. Dus als de tweede microflow was gestart nadat de eerste microflow had gecommit, dan zou het resultaat anders zijn geweest.
Een andere situatie is als twee gebruikers de children (of subchildren) van dezelfde parent op hetzelfde moment muteren. Nadat beide gebruikers hebben gecommit naar de database is er een mengvorm van beide gebruikersviews in de database ontstaan die elk van de gebruikers apart niet zo had gezien of bedoeld.
De oplossing van dit soort problemen is de implementatie van pessimistic locking of synchronisatie (semafoor). Een pessimistic lock wordt voorafgaand aan de verwerking geplaatst en slechts één microflow kan deze lock plaatsen. Een andere microflow kan deze lock dan niet meer plaatsen en kan de verwerking niet vervolgen en geeft een foutmelding of informatie wie de lock heeft geplaatst. Bij synchronisatie kan er maar één microflow tegelijk de semafoor passeren. Andere microflows moeten dan wachten tot de semafoor vrijgegeven wordt.
Mendix heeft een microflowproperty Disallow Concurrent execution. Dit mechanisme is niet flexibel en werkt niet bij horizontal scaling, omdat alleen in de master container wordt gecontroleerd. In de appstore is een module voor pessimistic locking beschikbaar. Java kent standaard implementaties voor synchronisatie en semaforen, maar die werken, net als Disallow Concurrent execution, alleen binnen dezelfde container. Een implementatie die wel werkt bij horizontal scaling, kan gebruik maken van de eigenschap van een update van hetzelfde record in de database. Zolang de transactie waarin de update zich bevindt niet is gecommit, zullen andere updates wachten.
Dit soort ontwerpfouten staat in de CWE-lijst op nummer 22 (Concurrent Execution using Shared Resource with Improper Synchronization ‘Race Condition’).
De implementatie in de Mendix runtime van het automatisch verwijderen van children via een associatie (cascading delete) is een voorbeeld van Read-Modify-Write probleem. Indien vóórdat het verwijderen van de parent, en als gevolg daarvan het automatisch verwijderen van de children, in de database is gecommit, in een andere microflow een object van diezelfde parent wordt gecreëerd, dan ontstaat een corruptie in de database. Het gecreëerde object verwijst naar een niet bestaande parent en wordt daarmee een wees. In vroege releases van versie 7 gaf dit zelfs een foutmelding elke keer als dit object werd gelezen.
In een volgende blog bespreken we de belangrijkste kwetsbaarheden in de browser.