Secure by Design - Hoofdstuk 2

Ingediend door Dirk Hornstra op 16-feb-2020 23:02

In januari zijn we gestart met hoofdstuk 1 (link). Net als dit hoofdstuk hoort het bij het blok "Introduction", dus we zitten nog in de licht verteerbare kost;

In dit hoofdstuk wordt het echt gebeurde verhaal beschreven van negatieve getallen, waardoor het bedrijf geld uitgaf in plaats van het ontving.
Het wordt omschreven als een boekhandel, maar in het echt was het een bedrijf actief in een andere sector.
Het test-team neemt de webserver onder vuur, begint met cookies te morrelen en één van de testers krijgt het lumineuze idee om -1 bij het aantal in te vullen. De order gaat door, hij hoeft niets te betalen. Het boek wordt geleverd en een creditnota wordt aangemaakt...

2.1 Uitleg ontstaan security-breach

Het betaalsysteem en debiteurenadministratie wordt onder de loep genomen.
Vanuit het betaalsysteem gezien hoeft er bij een negatief bedrag niets gedaan te worden, dus stopt met acties.
Vanuit de debiteurenadministratie gezien hebben klanten betaald of moeten ze nog betalen. Staat het bedrijf "in de schuld" bij de klant dan moet er een creditfactuur gemaakt worden (vaak automatisch). Het blijft vreemd dat het niet eerder opgemerkt is. We gaan door met voorraadbeheer en verzending.
De voorraad wordt afgeboekt met bestellingen (deze boeken zijn niet leverbaar, want al door een ander besteld) of toegevoegd als beschikbaar als de leverancier van de boeken deze bevestigd heeft. Bij deze order werd echter de voorraad met 1 verhoogd! Verzending werkt niet met negatieve getallen. Deze geven een exceptie en komen in het log. Maar als het log niet geraadpleegd wordt zal er geen haan naar kraaien...

Resultaat:

  • voorraadbeheer denkt ten onrechte dat er een boek extra is
  • debiteurbeheer denkt ten onrecht dat de klant nog geld terug moet krijgen

De systemen blijven hierdoor wel in balans want als een klant een boek zou kunnen verkopen aan deze boekhandel zou het inderdaad op deze manier werken. En hierdoor zal deze "bug" bij rapportages onzichtbaar blijven (totdat alle klanten op deze manier de boeken gaan kopen).

Pas bij de eindejaars-inventarisatie in het magazijn komt men erachter dat er boeken te weinig zijn.

Het blijkt dat er al een inventarisatie geweest was en er tekorten geconstateerd waren. Maar er werd vermoed dat de magazijnmedewerkers er een potje van maakten.
Oeps, er is meer aan de hand. Er zijn nog meer verschillen dan aan de hand van de creditnota's berekend wordt.
Het blijkt dat meerdere klanten een aantal boeken in de winkelwagen gooiden, er dan een -1 boek bij deden om zichzelf zo "korting" te geven.
Er moest dus wel betaald worden, maar niet het volledige bedrag. En hier is flink wat geld mee verloren.
Klanten zijn niet aangepakt hoewel ze eigenlijk frauduleus geacteerd hebben omdat het bedrijf bang was voor slechte publiciteit.

2.2 Ondiepe modellering

We gaan kijken hoe deze bug het systeem ingeslopen is. Iemand heeft ooit de keuze gemaakt om voor het aantal een integer te gebruiken. Doen wij dat niet allemaal...
Maar deze is niet beperkt op "moet groter dan 0 zijn".
Vaak ligt de meeste focus op "hoe moet mijn weergave van de echte wereld zijn", dus je krijgt orders, orderregels, boeken, aantallen.
Met integers, floats, booleans en strings heb je dit probleem "zo" opgelost.
Je hebt bepaalde concepten die expliciet zijn (objecten?) (order, orderregel, boek) en impliciete concepten (value-type) (aantal, titel, ISBN-nummer, prijs).
Als je dit zo snel er doorheen jast is aantal dus gewoon een integer. Aan je "order", "orderregel" wijdt je een class met eigenschappen en methoden, maar deze doe je even tussen neus en lippen door. Hoe dan?

Er komt een sketch over hoe een verkoper en programmeur de boel bespreken. Puntje hierbij: programmeur wil voor het bedrag een float gebruiken. Nooit doen, komt het boek in hoofdstuk 12 op terug. Titel en ISBN nummer worden door de programmeur als strings ingeschat. Maar ook ISBN heeft een eigen formaat. De focus van de programmeur ligt hier op bouwen en niet "hoe gaat/moet het werken".

Hetzelfde geldt voor creditcardnummers en BSN-nummers. Door ze als string te representeren kan er ongeldige data in staan en wat ook nog genoemd wordt, deze kunnen in logs terecht komen of zichtbaar gemaakt worden: dat wil je niet! In hoofdstuk 5 wordt dit verder besproken als read-once objecten (domain classes).

De functie om een klant aan te maken die getoond wordt heeft 8 parameters. Doordat dit value-types zijn kan de fout gemaakt worden dat waardes omgewisseld worden. "Per ongeluk" het BSN-nummer in het adres-veld... dat wil je niet.

2.3 Diepe modellering

De auteur is gecharmeerd van Domain Driven Design, Eric Evans heeft hier een boek over geschreven: Domain-Driven Design: Tackling Complexity in the Heart of Software. In overleg met de klant (collega's?) ga je een model opstellen wat een weergave is van wat er moet komen. Dit moet de focus krijgen en niet het "ik ga dit nu eerst bouwen zodat het volgens mij doet wat het zou moeten doen".

Nogmaals de sketch tussen verkoper en programmeur. De programmeur vraagt nu beter door, of er ook halve boeken gekocht kunnen worden, wat het maximum te bestellen boeken is. Juist omdat er blijkbaar restricties aan zitten maakt hij nu een eigen class. Bij de constructor van deze class (parameter aantal: int) heeft hij 2 controles op minimum en maximum. Binnen C# zouden dat volgens mij contracten zijn?

Maak het impliciete expliciet. Veel gehoord commentaar is dat je een flinke toename van het aantal classes krijgt.
Maar het voordeel daarvan is dan weer dat je centraal zaken afvangt en niet op tig plaatsen in je code controles hoeft uit te voeren.
Of als een maximum later verhoogt wordt dit op tig plaatsen hoeft aan te passen.
Als je een probleem voorgeschoteld krijgt kun je door eigen aannames denken: dit is makkelijk, maar bestaat dus het risico dat je zaken over het hoofd ziet. Vooral doorvragen dus!