Een bestandsupload brengt data van buiten je applicatie binnen en maakt die data onderdeel van je systeem. Lees hoe je bestandstype, grootte en inhoud controleert, opslag kiest en risico’s zoals malware, padmanipulatie en onveilige verwerking beperkt.
Begin met een dreigingsmodel voor bestandsuploads
Een veilige upload begint met bepalen wie bestanden kan aanbieden, welke bestandstypen nodig zijn en wat de applicatie daarna met die bestanden doet. Een profielfoto heeft een ander risicoprofiel dan een document dat automatisch wordt verwerkt of gedeeld met andere gebruikers. Breng daarom niet alleen het uploadformulier in kaart, maar ook alle vervolgstappen: opslaan, preview genereren, indexeren, downloaden en eventueel doorsturen naar andere systemen. Elk onderdeel kan een nieuwe ingang vormen.
Ga uit van bestanden die bewust schadelijk zijn samengesteld, ook wanneer de gebruiker een geldige account heeft. Een bestand kan bijvoorbeeld een parser laten crashen, onverwachte inhoud bevatten of misbruik maken van vertrouwen tussen gebruikers. Bepaal welke data vertrouwelijk is, wie uploads mag bekijken en of bestanden direct publiek toegankelijk moeten zijn. Beperk de toegestane bestandstypen tot wat de functionaliteit werkelijk vereist. Een algemene uploadfunctie die elk bestand accepteert, vergroot het aanvalsoppervlak en maakt controles moeilijker.
Leg deze keuzes vast als technische eisen. Denk aan maximale omvang, toegestane formaten, bewaartermijnen, toegangsregels en de manier waarop verdachte bestanden worden afgehandeld. Zo ontstaat een samenhangend ontwerp in plaats van een verzameling losse controles. Het dreigingsmodel verandert wanneer een bestand bijvoorbeeld door een achtergrondtaak wordt geopend of aan een externe dienst wordt aangeboden; neem die integraties vanaf het begin mee.
Controleer extensie, MIME-type en bestandsignatuur
Een bestandsnaam of extensie bewijst niet wat een upload werkelijk bevat. Een bestand met de naam rapport.pdf kan andere data bevatten, terwijl de client zelf het MIME-type opgeeft. Dat MIME-type is nuttig als eerste aanwijzing, maar is eveneens door de gebruiker te beïnvloeden. Gebruik daarom meerdere controles die elkaar aanvullen: controleer of de extensie op een expliciete allowlist staat, vergelijk het opgegeven MIME-type met de verwachte waarde en inspecteer de bestandsignatuur, ook wel magic bytes genoemd.
Geen van deze controles is op zichzelf afdoende. Een signatuur kan vervalst zijn en sommige geldige formaten hebben meerdere varianten. Controleer daarom ook of een gespecialiseerd parserpakket het bestand daadwerkelijk als het verwachte formaat kan lezen. Stel de parser zo in dat hij fouten en onverwachte structuur afwijst. Voor formaten met actieve inhoud, zoals bepaalde kantoorbestanden, is een geldige structuur bovendien geen bewijs dat de inhoud veilig is. Kies waar mogelijk een beperkt aantal formaten en accepteer geen onbekende varianten uit gemak.
Normaliseer extensies voordat je vergelijkt, bijvoorbeeld door hoofdletters gelijk te behandelen, maar vertrouw nooit op die normalisatie als beveiligingsgrens. Gebruik server-side regels; validatie in JavaScript verbetert hooguit de gebruikerservaring. Registreer afgewezen uploads met een reden die bruikbaar is voor diagnose, zonder het bestand of vertrouwelijke inhoud in algemene logs te plaatsen. Zo zijn fouten te onderzoeken zonder dat logging een nieuwe opslagplaats voor gevoelige data wordt.
Beperk bestandsgrootte en resourceverbruik
Een limiet op de bestandsgrootte beschermt niet alleen schijfruimte. Grote uploads kunnen verbindingen, geheugen, processortijd en wachtrijen bezetten. Stel daarom grenzen in op meerdere lagen: in de webserver of proxy, in het framework en in de applicatielogica. Als alleen de applicatie controleert, kan een verzoek al veel capaciteit hebben verbruikt voordat het wordt afgewezen. Stem de limieten op elkaar af en test hoe de infrastructuur reageert wanneer een verzoek de grens overschrijdt.
Een limiet per bestand voorkomt niet dat een gebruiker veel bestanden achter elkaar uploadt. Voeg waar passend beperkingen toe op aantal bestanden, totale omvang per verzoek en uploadfrequentie. Houd rekening met gelijktijdige uploads en met gebruikers die een verbinding onderbreken en opnieuw starten. Streaming naar opslag voorkomt dat een volledig bestand in het werkgeheugen moet passen, maar vereist wel zorgvuldige afhandeling van gedeeltelijke uploads en fouten. Verwijder onvolledige objecten wanneer het proces afbreekt.
De gecomprimeerde omvang zegt bovendien weinig over de kosten van verwerking. Een klein archief kan na uitpakken enorm worden; een afbeelding kan bij decodering veel geheugen innemen. Stel daarom afzonderlijke grenzen in voor uitgepakte omvang, aantal archiefitems, afbeeldingsafmetingen en verwerkingstijd. Stop verwerking wanneer een parser buiten de afgesproken grenzen komt. Deze limieten moeten passen bij de functie, niet bij de grootste bestanden die theoretisch mogelijk zijn. Een brede algemene limiet maakt misbruik eenvoudiger en kan bij piekbelasting alsnog tot capaciteitsproblemen leiden.
Inspecteer inhoud met parsers en malwaredetectie
Na type- en omvangscontrole moet de inhoud op een gecontroleerde manier worden onderzocht. Gebruik voor afbeeldingen, pdf’s, archieven en andere formaten bibliotheken die actief worden onderhouden. Een parser kan structurele afwijkingen herkennen die niet zichtbaar zijn in de bestandsextensie of signatuur. Houd rekening met parserfouten als een normaal onderdeel van de verwerking: vang fouten af, stop het proces en markeer het bestand als afgewezen of in quarantaine. Laat een ongeldige upload niet stilzwijgend doorgaan naar een preview- of indexeringsstap.
Malwarescanning kan aanvullend risico beperken, maar maakt een bestand niet automatisch veilig. Een scanner kan nieuwe of aangepaste dreigingen missen, en het scannen zelf verwerkt onbetrouwbare input. Beperk daarom de rechten van de scanner, houd definities en software actueel en bepaal wat er gebeurt als de scanner niet beschikbaar is. Voor gevoelige toepassingen is het vaak verstandiger een bestand pas vrij te geven nadat een scan is afgerond. Een time-out of fout moet niet ongemerkt worden behandeld als een schoon resultaat.
Voor bepaalde bestandstypen kan hercoderen veiliger zijn dan het origineel bewaren. Een afbeelding kan bijvoorbeeld worden gedecodeerd en opnieuw opgeslagen in een beperkt formaat, waarbij metadata en ingebedde inhoud worden verwijderd. Dat kan compatibiliteit of beeldkwaliteit beïnvloeden, dus test de gevolgen voor het gebruik. Archieven vragen afzonderlijke controles op geneste bestanden, dubbele namen en uitpakpaden. Houd originele uploads geïsoleerd en geef verwerkte bestanden pas toegang wanneer alle vereiste controles succesvol zijn afgerond.
Voorkom padmanipulatie en onveilige bestandsnamen
Een bestandsnaam die door een gebruiker wordt aangeleverd, is invoer en geen betrouwbare opslaglocatie. Namen met padcomponenten zoals ../, absolute paden, backslashes of platformafhankelijke tekens kunnen proberen buiten de bedoelde map te schrijven. Ook Unicode-normalisatie en dubbele extensies kunnen controles omzeilen wanneer validatie en opslag verschillende representaties gebruiken. Bouw daarom nooit rechtstreeks een serverpad op door een gebruikersnaam aan een directory te plakken.
Een gangbare aanpak is het genereren van een willekeurige, unieke opslagidentificatie aan de serverkant en die te gebruiken als fysieke naam. Bewaar de oorspronkelijke naam afzonderlijk als metadata, na passende normalisatie en lengtebeperking. Als de naam later in een webpagina of downloadheader verschijnt, codeer die dan voor de betreffende context. Padnormalisatie blijft nuttig als extra controle: bereken het uiteindelijke pad en verifieer dat het binnen de bedoelde opslagbasis valt. Deze controle vervangt echter niet het gebruik van servergegenereerde namen.
Let ook op overschrijven en racesituaties. Een voorspelbare naam kan bestaande bestanden vervangen, of twee gelijktijdige verzoeken kunnen dezelfde bestemming proberen te gebruiken. Gebruik opslagmethoden met exclusieve creatie of unieke sleutels en controleer fouten expliciet. Bij archieven moeten interne paden eveneens worden gevalideerd voordat bestanden worden uitgepakt; een veilig uploadpad voorkomt geen path traversal tijdens extractie. Geef foutmeldingen aan gebruikers geen interne paden of systeemdetails prijs. Die informatie kan aanvallers helpen de directorystructuur en gebruikte infrastructuur af te leiden.
Kies tussen lokale opslag en object storage
Lokale opslag kan geschikt zijn wanneer bestanden dicht bij de applicatie worden gebruikt en de infrastructuur overzichtelijk is. De applicatie beheert dan zelf directories, bestandsrechten, back-ups en capaciteit. In een omgeving met meerdere applicatie-instanties ontstaat echter de vraag welke instantie het bestand bezit en hoe andere instanties erbij kunnen. Een lokale schijf kan bovendien vollopen, verloren gaan bij vervanging van een server of onbedoeld via de webserver publiek worden aangeboden. Koppel lokale opslag daarom niet aan een publiek bereikbare map.
Object storage scheidt bestanden doorgaans van de applicatieservers en biedt schaalbare opslag met toegangsbeleid per bucket of object. Dat maakt het eenvoudiger om uploads buiten de webroot te houden en opslagcapaciteit onafhankelijk te beheren. Daar staan nieuwe aandachtspunten tegenover: bucketbeleid, sleutels, regioselectie, netwerktoegang en tijdelijke downloadlinks moeten correct worden ingericht. Een verkeerd publiek toegankelijke bucket kan alle uploads blootstellen. Controleer expliciet de standaardinstellingen van de gekozen dienst en geef applicatie-identiteiten alleen de benodigde rechten.
De keuze hangt ook af van verwerking en beschikbaarheid. Object storage werkt vaak goed met achtergrondtaken en directe uploads, maar introduceert asynchrone statussen en mogelijke vertraging tussen schrijven en uitlezen. Lokale opslag kan eenvoudiger zijn voor tijdelijke verwerking, maar vraagt een plan voor gedeelde toegang en herstel. Houd metadata, zoals eigenaar, status en opslaglocatie, in een database bij; gebruik de bestandsnaam niet als enige koppeling. Denk bij beide varianten aan versleuteling, back-ups, verwijdering en het gedrag wanneer opslag tijdelijk niet bereikbaar is.
Verwerk uploads geïsoleerd en buiten de webroot
Een bestand dat is geüpload, hoort niet automatisch uitvoerbaar of rechtstreeks benaderbaar te zijn. Sla het buiten de webroot op, of in een private object storage-bucket, en lever het pas via een gecontroleerde route aan gebruikers. Zo voorkomt de applicatie dat een server een upload als script uitvoert of dat een onbekende gebruiker een bestand kan openen door een URL te raden. Gebruik waar beschikbaar een aparte opslaglocatie of service-identiteit voor uploads, met beperkte lees- en schrijfrechten.
Ook achtergrondverwerking moet als een risicogrens worden behandeld. Een thumbnailgenerator, documentconverter of antivirusproces opent onbetrouwbare input en kan kwetsbare bibliotheken bevatten. Voer zulke taken uit met minimale privileges, beperkte netwerktoegang en grenzen voor CPU, geheugen en looptijd. Scheid tijdelijke werkbestanden van permanente opslag en ruim ze op na succes én na fouten. Als een proces vastloopt, mag dat niet onbeperkt workers of schijfruimte blijven bezetten.
Maak de status van een bestand expliciet, bijvoorbeeld ontvangen, in controle, goedgekeurd of afgewezen. Koppel toegang aan die status, zodat een upload niet beschikbaar wordt voordat noodzakelijke controles zijn afgerond. Bij downloads bepaalt de applicatie of de gebruiker toegang heeft en stelt zij passende headers in. Voor actieve bestandstypen kan aanbieden als download veiliger zijn dan inline weergave; voor andere bestanden kan een aparte domeinnaam helpen om cookies en applicatiecontext te scheiden. De juiste keuze hangt af van het formaat en het gebruik, maar directe uitvoering in de applicatieomgeving hoort geen impliciet gevolg van upload te zijn.
Beheer toegangsrechten, bewaartermijnen en auditsporen
Opslagbeveiliging omvat meer dan het afschermen van een directory of bucket. Bepaal per bestand wie het mag uploaden, inzien, vervangen en verwijderen. Controleer die rechten op het moment dat iemand een bestand opvraagt; een moeilijk te raden identificatie is geen vervanging voor autorisatie. Voorkom dat gebruikers door een ID in een URL te wijzigen bestanden van anderen kunnen openen. Beperk ook de rechten van achtergrondtaken en beheerdersinterfaces tot wat hun functie vereist.
Voor downloads zijn tijdelijke, ondertekende links soms praktisch, maar ze geven toegang aan iedereen die de link bezit zolang die geldig is. Beperk geldigheidsduur en rechten, voorkom dat links onnodig in logs of verwijzende headers terechtkomen en trek ze in waar de infrastructuur dat ondersteunt. Als de applicatie downloads zelf afhandelt, moet zij ook grote bestanden efficiënt streamen en toegangsbeslissingen consequent toepassen. Houd rekening met caching: een gedeelde cache mag een privébestand niet aan een andere gebruiker leveren.
Bewaar uploads niet langer dan de functie vereist. Stel regels op voor verwijdering van originelen, afgeleiden, tijdelijke bestanden en back-ups; anders kunnen verwijderde bestanden elders blijven bestaan. Auditlogs kunnen vastleggen wie een bestand uploadde, wijzigde, downloadde of verwijderde, met tijdstip en resultaat. Log geen bestandsinhoud, geheime tokens of onnodige persoonsgegevens. Beperk toegang tot logs en stel controles in op opvallende patronen, zoals herhaalde afwijzingen of ongebruikelijke downloadvolumes. Zo ondersteunen auditsporen onderzoek zonder zelf een extra lekbron te worden.
Veelgestelde vragen
Hoe beveilig ik een uploadformulier tegen CSRF-aanvallen?
Beveilig een uploadformulier met CSRF-bescherming en controleer server-side of het verzoek afkomstig is van een geautoriseerde gebruiker. Een aanvaller kan proberen een ingelogde gebruiker ongemerkt een uploadverzoek te laten versturen. Vertrouw daarom niet alleen op de aanwezigheid van een geldige sessiecookie.
- Gebruik een CSRF-token dat aan de gebruikerssessie is gekoppeld.
- Controleer waar passend de Origin- of Referer-header als aanvullende maatregel.
- Beperk wie uploads mag starten en vraag voor risicovolle acties eventueel om extra bevestiging.
- Test dat verzoeken zonder geldig token worden geweigerd.
Hoe richt ik directe uploads naar object storage veilig in?
Laat de server een kort geldige, beperkte uploadtoestemming uitgeven en laat de client daarmee uitsluitend één vooraf bepaalde upload uitvoeren. Zo hoeft de browser geen algemene opslagcredentials te ontvangen. De server blijft verantwoordelijk voor het vaststellen van eigenaar, bestemming en toegestane bestandsgrootte.
- Beperk de toestemming tot één object en de noodzakelijke schrijfhandeling.
- Gebruik een willekeurige objectnaam die de server genereert.
- Beperk de geldigheidsduur en stel waar mogelijk een maximale omvang in.
- Laat de applicatie na de upload de status controleren en pas daarna verdere verwerking starten.
- Voorkom dat de client objecten kan lezen, verwijderen of de bucket kan bekijken.
Welke HTTP-headers zijn belangrijk bij het aanbieden van geüploade bestanden?
Gebruik downloadheaders die voorkomen dat een bestand onverwacht als actieve inhoud wordt uitgevoerd of in een onveilige context wordt weergegeven. Stel de headers in op basis van het gevalideerde bestandstype, niet op basis van een waarde die de uploader zelf aanlevert.
- Gebruik voor risicovolle of onbekende formaten bij voorkeur Content-Disposition: attachment.
- Stel X-Content-Type-Options: nosniff in om onbedoelde MIME-type-detectie door browsers te beperken.
- Gebruik een passend Content-Type op basis van server-side validatie.
- Overweeg een aparte origin zonder applicatiecookies voor downloads die inline worden weergegeven.
- Controleer dat bestandsnamen in downloadheaders veilig worden gecodeerd.
Hoe test ik de beveiliging van een bestandsuploadfunctie?
Test de uploadfunctie met normale bestanden én met bewust afwijkende verzoeken, zodat je controleert of beveiligingsregels op de server daadwerkelijk worden afgedwongen. Alleen het uploadformulier testen is niet genoeg: ook verwerking, downloads en foutafhandeling moeten in de test vallen.
- Probeer bestanden met een misleidende extensie, verkeerd MIME-type en ongeldige inhoud.
- Test limieten met te grote bestanden, veel gelijktijdige uploads en afgebroken verbindingen.
- Controleer dat gebruikers elkaars niet-gedeelde bestanden niet kunnen uploaden, bekijken of verwijderen.
- Test archieven met geneste bestanden en verdachte paden in een gecontroleerde omgeving.
- Neem regressietests op voor eerder gevonden problemen en houd gebruikte parserbibliotheken bij.
Hoe verwijder ik geüploade bestanden betrouwbaar uit alle opslaglagen?
Verwijder een upload met een gecoördineerd proces dat zowel het bestand als de bijbehorende metadata en afgeleide bestanden afhandelt. Een databaseverwijzing wissen is niet voldoende als het object zelf, een thumbnail of een tijdelijke kopie blijft bestaan.
- Leg vast welke originele bestanden, previews en tijdelijke werkbestanden bij een upload horen.
- Voer verwijderingen waar nodig via een herhaalbare achtergrondtaak uit en registreer mislukte pogingen.
- Maak duidelijk hoe verwijdering uit back-ups en versiegeschiedenis wordt verwerkt.
- Controleer na verwijdering dat oude downloadlinks niet langer toegang geven.
- Stem bewaartermijnen af op het doel van de bestanden en toepasselijke privacy- en wettelijke verplichtingen.