sql-injisering

Dette må du vite om SQL-injisering – og slik beskytter du deg

Bildet er antagelig manipulert, men er et morsom eksempel på at det er viktig å behandle inndata med forsiktighet. Her forsøkes det angivelig å få en system for gjenkjenning av bilskilt til å slette en tabell i databasen. Bildet har sirkulert på nettet i alle fall siden 2008. Opphavet er ukjent.
Bildet er antagelig manipulert, men er et morsom eksempel på at det er viktig å behandle inndata med forsiktighet. Her forsøkes det angivelig å få en system for gjenkjenning av bilskilt til å slette en tabell i databasen. Bildet har sirkulert på nettet i alle fall siden 2008. Opphavet er ukjent.

IT-systemer kan angripes på en lang rekke måter. Men knapt noen er like enkle å utføre som det som gjerne kalles for SQL-injisering («SQLi – SQL injection»). Derfor er SQL-injisering blant de aller mest benyttede og effektive angrepsteknikkene som finnes, selv om det er relativt enkelt å beskytte seg mot slike angrep. SQL (Structured Query Language) er språket som vanligvis brukes i spørringer til relasjonsdatabaser.

I likhet med andre injiseringsangrep, dreier SQL-injisering seg om å få systemet til å tolke inndata som kode eller instruksjoner. Dette er mulig å få når inndataene er utilstrekkelig kontrollert.

Store lekkasjer

Som oftest når man hører om angrep basert på SQL-injisering, dreier det seg om relativt store datalekkasjer. Relativt ferske eksempler på dette, er databaselekkasjene hos britiske teleselskapet TalkTalk og brukerforumet til Ubuntu. Men det har blitt hevdet at SQL-injisering var blant teknikkene som ble brukt mot det Panama-baserte advokatfirmaet Mossack Fonseca, en sak som mest kjent som «Panama Papers», og enda mer nylig – mot databaser hos U.S. Election Assistance Commission.

Ved hjelp av SQL-injisering kan angripere ofte få tilgang i mengder av fortrolig informasjon som ellers ikke er tilgjengelig. Dette kan være informasjon som kan brukes direkte, som kredittnummer eller brukernavn og passord, men også informasjon som kan brukes til utpressing, for eksempel medlemsregistrene til porno- eller sjekketjenester.

Manipulering

Men SQL-injisering kan ikke bare brukes til å avsløre informasjon. Det kan like gjerne brukes til å manipulere eller slette data. Et klassisk eksempel på dette finnes på denne siden.

Mer alvorlig er det kanskje om en angriper har mulighet til å endre på for eksempel revisjonsdata eller databasebaserte aksesslogger for å skjule aktivitet, eller ved å endre på passordhashene til en bruker til noe man selv vet det tilsvarende passordet til. Mulighetene er mange.

SQL-injisering kan i utgangspunktet gjøres med enhver applikasjon som mer eller mindre ufiltrert inkluderer brukergenererte data i en spørring til en relasjonsdatabase. Men webapplikasjoner er spesielt utsatt, ikke minst fordi de ofte er tilgjengelige for alle.

Oppdagelsen

Oppdagelsen av denne typen sårbarheter og angrep har blitt kreditert sikkerhetsforskeren Jeff Forristal, som i 1998 dokumenterte at det var mulig med denne typen angrep på en Windows NT-basert webserver med SQL Server som databasesystem. Dette ble gjort i en artikkel i hackermagasinet Phrack. Forristal forteller om bakgrunnen for oppdagelsen i et intervju som er tilgjengelig her.

Den opprinnelige responsen fra Microsoft om problemet, som definitivt omfattet muligheten for SQL-injisering, var at det Forristal beskrev i artikkelen, ikke var noe problem. Det er uklart hvor lang tid det gikk før selskapet skiftet mening.

Enkelt eksempel

Tar man utgangspunktet i en webapplikasjon, som den offentlige delen nettavisen du nå leser, er det visse inndata som avgjør hvilket innhold som vises eller lagres. I noen tilfeller dreier det seg kanskje bare om én enkelt ID, som nummeret som er oppgitt i URL-en til artikkelen du besøker, for eksempel:

http://www.digi.no/artikler/nye-volvoer-far-innebygget-skype-for-business/366917

Trolig svært forenklet ligner databasekallet som gjøres for å vise artikkelen på noe tilsvarende dette:

SELECT * FROM artikler WHERE artikkelID=366917

Denne SQL-koden betyr at fra tabellen «artikler» skal verdiene i alle kolonnene (på grunn av jokertegnet *) i raden med artikkelID=366917, hentes.

Uten å ha noen direkte kunnskap om hvordan publiseringssystemet til digi.no er satt sammen, kan man enkelt finne ut at det er to deler av URL-en som påvirker databasekallene som gjøres før artikkelen vises.

Det første er «artikler». Endres denne verdien til noe annet, får man en feilmelding. Men kanskje, dersom systemet ikke er lagd med tanke på sikkerhet, kan dette i verste fall være en verdi som brukes til å fortelle hvilken tabell datakallet over skal gjøres mot. I så fall kan denne verdiene manipuleres, slik at hele databasekallet blir endret.

Men sannsynligvis er det slik at «artikler» er med i en begrenset hviteliste med verdier, og at verdien i URL-en bare er overførbar til tabellnavnet dersom verdien er nøyaktig lik en verdi i hvitelisten.

Den delen av URL-en som tilsvarer tittelen på artikkelen, ignoreres av publiseringssystemet. Den er primært til for å gi informasjon til brukeren og for å få bedre rangering i søkemotorer, som foretrekker slike URL-er.

Men den siste delen, nummeret, er helt vesentlig. Dette identifiserer selve artikkelen. Endrer man på dette til et annet nummer, blir man vist en annen artikkel – dersom det finnes en artikkel med denne ID-en.

Dette er en verdi som helt klar må brukes for å hente de riktige artikkeldataene fra databasen. Den vil derfor brukes direkte i databasekallet, som i eksempelet over.

Angrepet

I utgangspunktet er det nok bare heltallverdier som vil føre til treff, men sjekkes det ikke at verdien faktisk er et gyldig heltall, kan dette åpne for SQL-injisering.

La oss si at vi endrer URL-en vi brukte til denne:

http://www.digi.no/artikler/nye-volvoer-far-innebygget-skype-for-business/366917; DROP TABLE brukere /*

Er systemet enkelt laget og URL-endringene er tilpasset databasesystemet som benyttes, vil den tolke hele den siste delen av URL-en som ID-en til artikkelen. Dersom ID-en da blir brukt helt ufiltrert av webapplikasjonen, vil databasekallet bli noe slik som dette:

SELECT * FROM artikler WHERE artikkelID=366917; DROP TABLE brukere /*

Fortsatt hentes det data til den aktuelle artikkelen. Men deretter bes databasesystemet om å utføre enda en operasjon, nemlig å slette hele tabellen «brukere» (dersom den aktive databasebrukeren har rettigheter til å gjøre dette). Tegnene til slutt brukes til å kommentere ut eventuell kode som følger etterpå, slik at denne blir ignorert.

Dette vil kunne være en liten katastrofe for databaseeieren, men kan altså enkelt unngås ved at man først sjekker om ID-en til artikkel har en gyldig verdi, i dette tilfellet et heltall. Har den ikke den, vises en feilmelding, uten at databaseoppslaget utføres.

Et steg videre

En litt annen situasjon oppstår dersom slik SQL-injisering gjøres med en verdi som kan være tekst. Tekstverdier må vanligvis være satt inn i enkle anførselstegn som skilletegn. Samtidig kan det være at tekstverdien som kommer fra brukeren, inkluderer nettopp et enkelt anførselstegn. Uten noen form for filtrering av inndataene, kan dette skape trøbbel.

La oss si at det benyttes en URL hvor brukernavnet er oppgitt på denne måten:

http://domene.no/?brukernavn=olav

Denne kan potensielt utnyttes til å slette brukerdatabasen ved å endre URL-en til:

http://domene.no/?brukernavn='; DROP TABLE brukere /*

Dette betyr at brukernavnet det spørres etter, tilsynelatende er «'; DROP TABLE brukere /*»

Uten noen form for kontroll vil dette gi følgende databasespørring.

SELECT * FROM brukere WHERE brukernavn=''; DROP TABLE brukere /* '

Her vil databasesystemet søke etter kolonner hvor brukernavnet ikke er oppgitt (det er tomt mellom de to enkle anførselstegnene), men dessuten gjennomføre sletting av hele brukere-tabellen.

Dette var bare to grunnleggende eksempler på hvordan SQL-injisering kan utføres i usikrede systemer. Det finnes mange flere og mer avanserte. Selskapet Netsparker kom i begynnelsen av desember med en oppdatert oversikt over mange varianter på slike angrep som kan utføres på noen av de vanligste databasesystemene, MySQL, Oracle Database, PostgreSQL og SQL Server.

I stedet for å gå i detalj på alle disse, skal vi heller ta en titt på hva som kan gjøres for å forhindre slike angrep.

Forsvarsmiddel

Det mange gjør, og kanskje nøyer seg med, er å sjekke at inndataene er av riktig type eller format, for eksempel at epostadresser blant annet inneholder en krøllalfa (@).

Så opplever man at kanskje at dette ikke strekker til, og så tar man i bruk innebygd eller egenkomponert escape-funksjonalitet for å hindre at spesielle tegn som kan åpne opp for SQL-injisering – slik som det enkle anførselstegnet nevnt over – blir tolket som noe annet enn dataverdier. Nøyaktig hvordan escapingen utføres, er avhenger av databasesystemet. Men vanligvis settes det inne et escape-tegn, for eksempel en \, foran tegn som ', ", \, & og _. Hvilke tegn som må escapes, avhenger av også databasesystemet.

Dette er effektivt i veldig mange tilfeller, men det er fort gjort å miste oversikten over hvilke inndata som har blitt sikret. Blant annet OWASP (Open Web Application Security Project) mener at escaping kun bør brukes som siste utvei, siden teknikken anses som svakere og mindre sikker enn andre og bedre metoder.

Så hva bør man egentlig gjøre?

Førstevalget til blant annet OWASP er det som kalles «prepared statements» med parameteriserte spørringer eller variabelbinding. Prosjektet mener at dette er metoden alle utviklere først bør lære å bruke, når de opplæres i å skrive databasespørringer.

Det er flere fordeler med å bruke prepared statements, men i konteksten til artikkelen er den store fordelen at databasesystemet gjøres langt bedre i stand til å skille mellom kode og data.

I stedet for å sette en SQL-spørring sammen som en eneste lang tekststreng, så definerer utviklere all SQL-koden i en streng, men setter av plass til inndataene, typisk ved å bruke en spørsmålstegn (?) som plassholder, i stedet for at inndataene settes rett inn i strengen.

Deretter oppgis hver av inndataene som en separat parameter til forberedte uttrykket. Nedenfor vises et eksempel på hvordan den enkle eksempelet med brukernavn vi brukte tidligere i saken, vil se omtrent slik ut i Java med bruk av prepared statements.

String brukernavn = request.getParameter("brukernavn"); // bør uansett valideres førstString query = "SELECT * FROM brukere WHERE brukernavn= ? "; // SQL-uttrykket defineresPreparedStatement pstmt = connection.prepareStatement( query );pstmt.setString( 1, brukernavn); /* verdien av variabelen brukernavn erstatter den første plassholderen i spørringen query.Det er også oppgitt at variabeldataene skal tolkes som en tekststreng. */ResultSet results = pstmt.executeQuery( ); // resultatet fra databasen tas imot

Uansett hva en ondsinnet bruker forsøker å sette inn som verdi for variabelen brukernavn, vil dette ved bruk metoden beskrevet over, bli tolket som rene data og ikke som kode som endrer formålet med spørringen.

Faren for at man glemmer escaping av enkeltdata eller gjør escapingen på feil måte, fjernes helt med prepared statements fordi dette gjør escapingen unødvendig.

URL-en http://domene.no/?brukernavn='; DROP TABLE brukere /* vil dermed føre til at databasesystemet ikke vil tolke «DROP TABLE brukere» som et nytt uttrykk, men i stedet vil søke etter brukernavnet «'; DROP TABLE brukere /*», som det høyst sannsynlig ikke vil finne.

Prepared statements støttes av programmeringsgrensesnitt som tilbys i de fleste programmeringsspråk og til de vanligste databasesystemene.

Hvitelisting kan være nødvendig

Det er noen deler av SQL-spørringene hvor parametre eller bundne variabler ikke kan brukes. Dette gjelder blant annet tabell- og kolonnenavn. For eksempel vil ikke uttrykket nedenfor (skrevet i PHP med bruk av programmeringsgrensesnittet PDO) fungere, fordi man forsøker å oppgi tabellnavnet som en parameter.

$sth = $dbh->prepare('SELECT navn, farge, kalorier FROM ? WHERE kalorier < ?');

Dersom tabellnavnet her avhenger av inndataene, må man i stedet benytte en ordinær variabel for å angi dette.

$sth = $dbh->prepare('SELECT navn, farge, kalorier FROM '.$tabellnavn.' WHERE kalorier < ?');

Dersom tabellnavnet blir hentet direkte fra inndataene, må man i alle fall sørge for at navnet som er oppgitt er identisk med en hvitelistet verdi, slik at ikke ikke-validerte inndata fra brukeren ender opp i spørringen.

Til tross for disse relativt enkle metodene, kommer det hele tiden nye og vellykkede angrep. Wikipedias eksempelliste er kanskje ikke den mest oppdaterte, men den viser at det langt fra bare er utviklere av små og ubetydelige nettsteder som ikke har sikret løsningen godt nok mot SQL-injisering. Selv databaseleverandørene Microsoft og MySQL har tidligere gått i denne fellen. Det er likevel ingen grunn til at utviklere fortsetter å gjøre de samme feilene.

Leste du denne? Dette må du vite om «Cross-Site Scripting»-angrep – og slik beskytter du deg

Powered by Labrador CMS