Hoe een DDoS-aanval werkt en hoe netwerken zich verdedigen
Een DDoS-aanval is het digitale equivalent van een menigte die een winkeldeur blokkeert: niemand hoeft in te breken, de zaak ligt plat doordat er te veel mensen tegelijk voor de deur staan. De afkorting staat voor Distributed Denial of Service. Verdeeld, omdat het verkeer van duizenden bronnen komt, en Denial of Service omdat het doel niet binnenkomen is maar de dienst onbereikbaar maken.
Wat die aanvallen zo lastig maakt, is dat elk pakketje er op zichzelf onschuldig uitziet. Een DNS-vraag, een halve TCP-verbinding, een HTTP-verzoek: stuk voor stuk normaal verkeer. Ik pluis de drie manieren uit waarop zo'n aanval werkt: ik meet een versterkingsaanval met dig, bouw een SYN-flood na in een testnetwerk tot de server geen echte verbinding meer accepteert, en loop daarna langs de verdediging.
Drie manieren om een server te verdrinken
Grofweg vallen DDoS-aanvallen in drie categorieën. De eerste is volumetrisch: genoeg bits per seconde om de internetverbinding van het slachtoffer te verstoppen voordat het verkeer de server zelfs maar bereikt. De tweede mikt op de toestand die een protocol bijhoudt, zoals de halfopen verbindingen van een TCP-handshake. De derde, de applicatielaag, stuurt verzoeken die er geldig uitzien maar die de server veel werk kosten.
Reflectie en amplificatie: klein pakket, groot antwoord
Bij UDP hoeft een afzender geen verbinding op te bouwen, dus kan een aanvaller in het pakket een vals bronadres zetten: dat van het slachtoffer. Elk antwoord belandt dan bij de pechvogel wiens adres is misbruikt. Vervelend wordt het als die antwoorden groot zijn. Sommige diensten geven op een kort vraagje een lang antwoord, en zo heb je een versterker: de aanvaller stuurt een paar bytes, de nietsvermoedende server stuurt een veelvoud daarvan naar het slachtoffer.
DNS is het klassieke voorbeeld. Ik vroeg vanaf een Nederlandse verbinding de DNSSEC-sleutels van de rootzone op bij een publieke resolver. De vraag is 28 bytes, het antwoord 1139: een factor 40, en dat is nog bescheiden. De Amerikaanse CISA houdt versterkingsfactoren per protocol bij. NTP haalt met het oude monlist-commando een factor 556, en een memcached-server die per ongeluk vanaf het internet bereikbaar is zit tussen de 10.000 en 51.000. Dezelfde eigenschappen die DNS in tientallen milliseconden een naam laten vinden, een lichte vraag zonder handshake, maken het een ideale versterker zodra iemand het bronadres vervalst.
De halfopen verbinding: de SYN-flood
TCP kent wel een handshake, zoals in wat er gebeurt vóór de eerste byte: de client stuurt een SYN, de server antwoordt met SYN-ACK, de client bevestigt met ACK. Pas daarna staat de verbinding.
Het probleem zit in de tussenstap. Als de server de SYN ontvangt, reserveert hij geheugen en wacht op de laatste ACK. Die halfopen verbindingen gaan in een wachtrij met een vaste grootte. Stuur je genoeg SYN-pakketten met valse bronadressen die nooit de ACK sturen, dan loopt die rij vol en is er geen plek meer voor echte bezoekers.
Ik heb dat nagebouwd in een afgeschermd testnetwerk met twee namespaces. Vanuit de ene kant vuur ik SYN-pakketten af met willekeurige bronadressen die nooit terugantwoorden, zo'n duizend per seconde. Dat is geen zware aanval. Toch loopt een gewone verbinding die ik halverwege probeer te maken vast:
syncookies 0
half-open (SYN-RECV) in de wachtrij: 6
gewone verbinding: time-out na 6.01s
SYN gedropt door volle wachtrij (TCPReqQFullDrop): 5691
De teller TCPReqQFullDrop liep met 5691 op: zoveel SYN-pakketten gooide de kernel weg omdat de rij vol zat. Mijn eigen verbinding was daar één van. De server stond niet onder zware belasting, de rij met halve verbindingen was simpelweg bezet.
SYN-cookies: onthouden zonder geheugen
De oplossing is slim en staat in RFC 4987 uit 2007. In plaats van geheugen te reserveren bij de eerste SYN, stopt de server alle informatie die hij nodig heeft in het volgnummer dat hij in de SYN-ACK terugstuurt. Dat volgnummer is een cookie: een teller die elke 64 seconden opschuift, een paar bits voor de maximale pakketgrootte, en een hash van de adressen, poorten en een geheim dat alleen de server kent.
Komt er later een ACK terug, dan rekent de server de cookie opnieuw uit. Klopt hij, dan bouwt hij de verbinding alsnog op. Tot dat moment hoeft hij niets te onthouden, dus de wachtrij kan niet vollopen. In mijn test zette ik syncookies op 0 om de aanval te laten slagen. Op deze machine staat hij standaard op 1: Linux zet cookies automatisch in zodra de wachtrij overloopt. De prijs is dat een deel van de TCP-opties niet in zo'n cookie past, een kleine concessie tijdens een aanval.
De applicatielaag: verzoeken die er echt uitzien
Filteren wordt moeilijker als het verkeer volledig legitiem is. Een HTTP-aanval opent nette verbindingen en vraagt een echte pagina op, maar doet dat zo vaak dat de server of de database het niet bijbeent. Elk verzoek op zich lijkt op een gewone bezoeker.
Deze aanvallen komen van botnets: grote aantallen gekaapte apparaten. Cloudflare beschreef eind 2025 een botnet van naar schatting één tot vier miljoen besmette hosts, grotendeels Android-tv-kastjes, dat op zijn piek 205 miljoen verzoeken per seconde genereerde. Die adressen zijn echt, dus wegfilteren op bronadres helpt niet. Je moet naar het gedrag kijken.
Hoe groot het echt wordt
De cijfers zijn absurd geworden. In juni 2025 blokkeerde Cloudflare een aanval van 7,3 terabit per seconde die 45 seconden duurde. In die drie kwart minuut werd 37,4 terabyte afgeleverd, van ruim 122.000 bronadressen in 161 landen. Bijna alles, 99,996 procent, was UDP-flood. Een half jaar later stond het record op 31,4 terabit per seconde, in slechts 35 seconden.
Dat laatste is typerend: de meeste aanvallen zijn kort. Een halve minuut volstaat om een dienst even onbereikbaar te maken. Cloudflare mitigeerde over heel 2025 ruim 47 miljoen aanvallen.
Verdedigen: filteren, zwartgaten en schoonspoelen
De eerste verdediging is de bron. Als elke provider alleen pakketten doorlaat met een afzender die bij het eigen netwerk past, kan niemand nog een vals bronadres gebruiken. Dat principe staat al sinds 2000 in BCP 38 (RFC 2827), maar werkt alleen als iedereen meedoet. Daarom bestaat reflectie nog steeds.
Aan de kant van het slachtoffer zijn er grovere middelen. Bij remotely triggered blackholing vraagt een netwerk zijn buren om al het verkeer naar een bepaald adres weg te gooien. Dat gebeurt via BGP, met een community-waarde die in RFC 7999 is vastgelegd als BLACKHOLE. Het nadeel is wreed: je gooit het aangevallen adres zelf ook offline. Je redt de rest van je netwerk door het doelwit op te offeren.
Subtieler is schoonspoelen. Het verkeer wordt omgeleid naar een scrubbing center dat het vuil eruit filtert en het schone deel over een apart kanaal terugstuurt. In Nederland draait de non-profit NBIP zo'n gezamenlijke wasstraat, de NaWas, waar meer dan 150 providers tijdens een aanval verkeer naartoe sturen. En wie zijn dienst via anycast over veel locaties verspreidt, verdeelt een volumetrische aanval automatisch over al die plekken.
Zelf proberen
De versterking kun je zelf meten. Deze vraag naar de DNSSEC-sleutels van de rootzone levert een antwoord op dat tientallen keren groter is:
$ dig +dnssec +nocookie DNSKEY . @1.1.1.1 | grep SIZE
;; MSG SIZE rcvd: 1139
De vraag zelf is 28 bytes (zichtbaar met +qr), het antwoord 1139. Factor 40, vanaf één commando. Stel je voor dat het bronadres niet van jou was.
Of lees uit welke verdediging je eigen Linux-machine klaar heeft staan tegen een SYN-flood:
$ cat /proc/sys/net/ipv4/tcp_syncookies
1
Een 1 betekent dat de kernel SYN-cookies inzet zodra de wachtrij vol dreigt te raken: precies de bescherming die ik in mijn test moest uitzetten om de aanval te laten slagen.
Tot slot
Wat me bij het nabouwen verraste, was hoe weinig geweld een SYN-flood nodig heeft. Ik had een stortvloed verwacht, maar duizend pakketjes per seconde naar een standaard wachtrij hield mijn eigen verbinding al zes seconden buiten. De bytes zelf stelden niets voor. De aanval werkte op een limiet die je niet ziet tot hij vol is. De terabit-aanvallen halen de krantenkoppen, maar het principe eronder is bijna altijd hetzelfde goedkope trucje: een adres dat niet klopt, of een rij die vol raakt.