De siste årene har vi sett flere massive SQL-injection- og Cross Site Scripting (XSS)-angrep mot store siter. Twitter hadde nylig en XSS-feil og i begynnelsen av juni var det et stort angrep mot siter som kjørte en bestemt annonsekomponent. Beskyttelse mot denne type angrep, krever at man tenker på kontekst når man skriver kode.
Validering av input er ikke løsningen
Tidligere snakket man ofte om at manglende validering av input var årsaken til disse feilene. Dette er dessverre ikke riktig. Validering av input handler om å sjekke at input er riktig i forhold til domenet. Dette vil riktignok stoppe mange angrep, men på langt nær alle. Et godt eksempel er navnet “Ken-Martin O’Conner”. Dette etternavnet inneholder flere tegn som også er kontrolltegn i SQL. For å kunne godta dette navnet må man ved validering av input godta både bindestrek og fnutter. Dermed vil man også tillate et angrep som ' OR '' LIKE '' --. Man kan selvsagt forsøke å gjøre valideringen enda strengere, men man risikerer kjapt å avvise gyldig input.
Jeg sier ikke at validering av input er unødvendig – det er bare ikke løsningen på dette problemet.
Kontekst og escaping
Problemet ved både XSS- og SQL-injection-feil er at data flyter fra en kontekst og over i en annen. Ved overgang fra f.eks. javakode til SQL, må vi sørge for at vi beholder semantikken. Skal vi sette inn en datastreng i en SQL-spørring, må vi sørge for at det forblir en streng. Dette gjør vi ved å foreta output escaping. For SQL er løsningen enkelt og greit å ta i bruk parameteriserte spørringer (prepared statements). Bruker vi prepared statements og gir inn parametrene i etterkant, vil rammeverket håndtere escaping av kontrolltegn for oss.
For XSS er det desverre noe vanskeligere. Når data forlater vår applikasjon og går over til noe browseren skal tolke, er det nemlig flere mulige kontekster. Vi kan skrive ut innhold i javascript, javascript i HTML, mellom HTML-tagger, i HTML-attributter osv. Og flere av disse kontekstene må behandles forskjellig. Vi kan begynne med input som skrives ut mellom HTML-tagger. Dette er den enkleste konteksten. Vi kan bruke dette eksempelet:
<div>Du søkte på "her kommer inputten"</div>
For å hindre kjøring av script her, må vi sørge for at input ikke kan åpne nye tagger, f.eks. en <script>-tag. Dette gjør vi selvsagt da ved å erstatte > med > og < med <.
I HTML-attributter, må vi også passe oss for enkle eller doble fnutter alt etter hva som er brukt. Et eksempel på denne konteksten kan være:
<input type="text" value="her kommer inputten" name="searchValue" />
Et typisk angrep ville i dette tilfellet ha brutt ut av value-attributtet og åpnet en javascript-event (f.eks. onclick eller onmouseover). Her må vi altså " med "e; og ' med ' eller '. Men i noen tilfeller glemmer utviklere å bruke fnutter til å avgrense attributtverdier, så til og med et mellomrom kan lede til problemer.
Ser vi på javascript i HTML, kan det se slik ut:
... <body> <script> var searchTerms = 'her kommer inputten'; </script><code> ... </body> ...
Her er det kanskje lett å tenke at det holder å legge til en backslash forann eventuelle enkle fnutter (' blir til \'), men vi må huske at vi er i en såkalt dobbel-kontekst: Javascript-kontekst inni en HTML-kontekst. Vi må derfor også passe oss for HTML-tegn. Her er et eksempel på hvorfor man må escape for begge kontekster.
For mer informasjon om de forskjellige XSS-kontekstene, se OWASP XSS Prevention Cheat Sheet.
For .NET finnes det et bibliotek kalt Web Protection Library. Dette biblioteket har en komponent som heter AntiXSS, som kan escape for forskjellige kontekster. For Java kan man f.eks. benytte encoderen i ESAPI.
Oppsummert
Beskyttelese mot disse angrepene må gjøres med kontekstbasert escaping idet vi bytter fra en kontekst til en annen.