Forhindre kodeinjeksjon

Ofte ser nettsteder ut til å eksistere først og fremst for å sette noe inn i en database for å trekke den ut senere. Mens andre databasemetoder, for eksempel NoSQL, har blitt populær de siste årene, ligger data for mange nettsteder fremdeles i den tradisjonelle SQL-databasen. Disse dataene består ofte av verdifulle personlige opplysninger som kredittkortnumre og annen personlig informasjon av interesse for identitetstyver og kriminelle. Hackere ser derfor alltid ut til å få disse dataene. Et av de vanligste målene for disse angrepene er SQL-databasene som ligger bak mange webapplikasjoner gjennom en prosess med SQL Injection.

Et injeksjonsangrep virker ved å få søknaden til å sende usikker inntasting til tolken. Nylig er 40 000 kundeoppføringer tatt fra Bell Canada et resultat av et SQL-injeksjonsangrep. I slutten av 2013 stjal hackere over $ 100 000 fra en California-basert ISP ved hjelp av SQL-injeksjon.

Open Web Application Security Project (OWASP) valgte injeksjonsangrepet som den første applikasjonssikkerhetsrisikoen i topp ti i 2013, basert på utbredelsen og risikoen for de angripne websystemene. Dessverre holdt den også nummer én plassering i forrige rapport fra 2010. Så hva er et SQL-injeksjonsangrep? I denne opplæringen diskuterer jeg hvordan de fungerer, og hva du kan gjøre for å beskytte applikasjonen mot disse angrepene.

Hva er et injeksjonsangrep?

Ethvert tolket system bak en webserver kan være målet for et injeksjonsangrep. De vanligste målene er SQL-databaseserverne bak mange nettsteder. SQL-injeksjon skyldes ikke direkte svakheter i databasen, men bruker åpninger i applikasjonen slik at angriperen kan utføre uttalelser om angriperens valg på serveren. Et SQL-injeksjonsangrep får databasen til å dele mer informasjon enn programmet er utformet for å gi. La oss se på en SQL-databasesamtale som du kan skrive i ASP.NET.

SqlCommand command = new SqlCommand ("SELECT * FROM userdata WHERE UserId =" + id); SqlDataReader reader = command.ExecuteReader ();

Hva er galt med denne koden? Kanskje ingenting. Problemet er det id streng vi bruker. Hvor kommer den verdien fra? Hvis vi genererer det internt eller fra en klarert kilde, kan denne koden fungere uten problemer. Men hvis vi får verdien av en bruker og bruker uten endring, har vi nettopp åpnet oss opp til SQL-injeksjon.

La oss ta en vanlig sak der vi får parameteren til å slå opp som en del av nettadressen. Ta nettadressen http://www.example.com/user/details?id=123. I ASP.NET ved hjelp av C # kan vi få den bestått verdien ved hjelp av denne koden:

streng id = Request.QueryString ["id"];

Denne koden kombinert med samtalen ovenfor overlater oss til et angrep. Hvis brukeren sender et bruker-ID som 123 som forventet, fungerer det bra. Men ingenting vi har gjort her sikrer at dette er tilfelle. La oss si at angriperen prøver å få tilgang til nettadressen http://www.example.com/user/details?id=0; VELG * FRA userdata.

I stedet for bare å overføre en verdi som forventet, har vi gitt en verdi og deretter lagt til et semikolon, som avslutter en SQL-setning. Det legger deretter til en andre SQL-setning som angriperen ønsker å kjøre. I dette tilfellet ville det returnere alle poster i userdata tabellen. Avhengig av resten av koden på vår side, kan det returnere en feil eller kanskje vise hver post i databasen til angriperen. Selv en feil kan brukes med nøye konstruerte søk for å bygge en oversikt over databasen. Verre, tenk at angriperen går til en URL til http://www.example.com/user/details?id=0; DROP TABLE userdata. Nå er alle brukerne dine tapt.

I dette eksempelet kan angriperen kjøre hvilken som helst kode de vil ha på databaseserveren. Hvis kontoen som databasene ringer under, har full kontroll over databasen, et altfor vanlig scenario, og deretter å droppe tabeller og slette poster, er en enkel måte å få ned et nettsted på. Selv om angriperen bare kan lese og skrive data i databasen, er pulsdata bare et spørsmål om tålmodighet og omsorg.


Ta en enkel database med navnet "Produkter" bestående av tre kolonner. Den første kolonnen inneholder produkt-ID, den andre inneholder produktnavnet, og den tredje holder produktets pris. For vårt vanlige spørsmål forsøker vi å finne alle produkter som inneholder widgeten i navnet. SQL-en for å gjøre dette i samme mønster som vi har vist, ser slik ut:

streng sql = "VELG * FRA PRODUKTER WHERE produktnavn LIKE '%" + søkeord + "%'";

Et typisk nettsted for å få tilgang til dette ville se ut http://www.example.com/product?search=widget. Her vil nettsiden simpelthen sykle gjennom hver returrekord og vise den på skjermen. I dette tilfellet ser vi vårt widgetprodukt.

 | productid | produktnavn | pris | | ----------- | 1 | Widget | 100,00 |

La oss endre vår forespørsel til http://www.example.com/product?search=widget 'ELLER 1 = 1;-- og utfør det samme spørsmålet. se noe mer. Faktisk ser vi alle poster i tabellen.

 | productid | produktnavn | pris | | --- | 1 | Widget | 100,00 | | 2 | Thingy | 50,00 | | 3 | Boxy | 125,00 |

Vi har lurt tolken til å kjøre SQL-kode av vårt valg. Den resulterende SQL-utført vil være:

SELECT * FROM Products WHERE produktnavn LIKE '% widget' ELLER 1 = 1; -% '

Resultatet blir to uttalelser:

VELG * FRA PRODUKTER WHERE produktnavn LIKE '% widget' ELLER 1 = 1; -%'

Ved å legge til 1 = 1, som alltid er sant, hvor klausulen vil være sant for hver rad i bordet og den resulterende spørringen kommer tilbake hver rad. De -- ved starten av den andre setningen blir resten av SQL-setningen til en kommentar som forhindrer feilmeldingen vi ellers ville se.

Endringer i skjermen kan ikke hindre denne utsagnet. En pasient angriper kan ikke bruke enda mer enn det faktum en verdi returneres eller ikke og nøye konstruerte spørringer for å sakte kartlegge databasen din og muligens hente data selv om de bare ser en feilmelding. Det finnes verktøy som sqlmap for å automatisere prosessen.

Mitigating SQL Injection

Du forhindrer SQL-injeksjon ved å forhindre uprøvd inngang fra å komme til SQL-databasen eller andre tolk. Eventuell inngang fra utsiden av systemet bør betraktes som usikker. Selv data fra andre partnersystemer må betraktes som usikre, da du ikke har mulighet til å garantere at det andre systemet ikke lider av sikkerhetsproblemer som vil tillate innføring av vilkårlig data, og deretter overføres til søknaden din.

Hvis vi vet at id-parameteren alltid skal være et heltall, kan vi prøve å konvertere det til et heltall og vise en feil hvis dette mislykkes. Et ASP.NET MVC-program ville gjøre dette ved å bruke kode som ligner på dette:

int kvantitet; hvis (! int.TryParse (Request.QueryString ["qty"], ut kvantitet)) return RedirectToAction ("Ugyldig"); 

Dette vil forsøke å konvertere strengen til et heltall. Hvis konverteringen mislykkes, omdirigerer koden til en handling som vil vise en ugyldig melding.

Dette kan forhindres hvis vi leter etter et heltallsparameter. Det ville ikke hjelpe hvis vi forventer tekst som i det tidligere produktsøket. Den foretrukne teknikken i dette tilfellet er å bruke vanlige uttrykk eller strengutskiftninger for å tillate bare nødvendige tegn i den bestått verdien.

Dette kan gjøres ved whitelisting, prosessen med å fjerne andre tegn enn et spesifisert sett, eller svarteliste, prosessen med å fjerne medlemmer av et spesifisert sett fra strengen. Whitelisting er mer pålitelig siden du angir bare tillatt tegn. For å fjerne alt annet enn bokstaver og tall i en streng, kan vi bruke kode som:

Regex regEx = ny Regex ("[^ a-zA-Z0-9 -]"); streng filteredString = regEx (originalString, "");

Du må evaluere en hvilken som helst innspilling. Noen databasespørsmål kan trenge mer spesialisert oppmerksomhet. Ta tegn som kan være meningsfylt i en SQL-kommando, men det kan også være et gyldig tegn i en databasesamtale. For eksempel er det enkle sitat tegnet 'brukt til å starte og avslutte en streng i SQL, men kan også være en del av en persons navn som O'Conner. I dette tilfellet erstatter det enkelte sitatet ' med påfølgende enkelt anførselstegn " kan eliminere problemet.

Lagrede prosedyrer

Lagrede prosedyrer blir ofte sett på som en løsning for dette problemet, og de kan være en del av løsningen. Men en dårlig skrevet lagret prosedyre vil ikke spare deg. Ta denne lagrede prosedyren som inneholder kode som ligner på det vi allerede har sett for å bygge et spørsmål:

ALTER PROCEDURE [dbo]. [SearchProducts] @searchterm VARCHAR (50) = "AS BEGIN DECLARE @ spørring VARCHAR (100) SET @query = 'VELG * FRA Produkter WHERE produktnavn LIKE"%' + @searchterm + '% "; EXEC (@query) END

Strengenforbindelsen her er problemet. La oss prøve et enkelt forsøk der vi prøver å sette en alltid sant tilstand for å vise alle rader i tabellen og passere i samme "widget" ELLER 1 = 1; - "som spørringen vi så tidligere. Resultatet er også det samme :

 | productid | produktnavn | pris | | - | 1 | Widget | 100,00 | | 2 | Thingy | 50,00 | | 3 | Boxy | 125,00 |

Hvis usikre data blir sendt inn, har vi det samme resultatet som om samtalen ble opprettet i vår kode. At strengkonsentrasjonen foregår i en lagret prosedyre i stedet for i vår kode, gir ingen beskyttelse.

parametrisering

Det neste stykket av puslespillet for å beskytte mot injeksjonsangrep kommer inn ved å bruke parameterisering. Å bygge SQL-spørringer ved å sammenkoble strenger og deretter passere den ferdige koden, gir ikke databasen noen ide om hvilken del av strengen som er en parameter og hva er en del av kommandoen. Vi kan bidra til å beskytte mot angrep ved å lage SQL-anrop på en måte som holder utsagn og verdier tydelige.

Vi kan omskrive den lagrede prosedyren som ble vist tidligere for å bruke parametere og produsere et sikrere anrop. I stedet for å sammenkoble % tegn som representerer jokertegn, vil vi opprette en ny streng som legger til disse tegnene og deretter sende denne nye strengen som en parameter til SQL-setningen. Den nye, lagrede prosedyren ser slik ut:

ALTER PROCEDURE [dbo]. [SearchProductsFixed] @searchterm NVARCHAR (50) = "AS BEGIN DECLARE @ spørre NVARCHAR (100) DECLARE @msearch NVARCHAR (55) SET @msearch = '%' + @searchterm + '%' SET @query = 'VELG * FRA PRODUKTER WHERE produktnavn LIKE @search' EXEC sp_executesql @query, N '@ søk VARCHAR (55)', @msearch END

Å utføre denne lagrede prosedyren med bare orddisplayet virker som forventet.

 | productid | produktnavn | pris | ---- | 1 | Widget | 100,00

Og hvis vi passerer med parameterparameteren "ELLER 1 = 1; - blir det ikke noe som blir returnert, og vi er ikke lenger sårbare overfor dette angrepet.


Parameterisering krever ikke lagrede prosedyrer. Du kan også dra nytte av det med spørringer som er bygget i kode. Her er et kort segment i C # for å koble til og kjøre en forespørsel mot Microsoft SQL-server ved hjelp av en parameter.

const string sql = "SELECT * FRA Produkter WHERE produktnavn LIKE @CategoryID"; var connString = WebConfigurationManager.ConnectionStrings ["ProductDatabase"]. ConnectionString; bruker (var conn = ny SqlConnection (connString)) var kommando = ny SqlCommand (sql, conn); command.Parameters.Add ("@ searchterm", SqlDbType.NVarChar) .Value = string.Format ("% 0%", søkTerm); command.Connection.Open (); SqlDataReader reader = command.ExecuteReader (); mens (leser.NesteResultat ()) // Koden som skal utføres på hver linje, går her

Minste privilegium

Opp til dette punktet har jeg vist hvordan å redusere databaseangrep. Et annet viktig lag av forsvar, minimerer skadeårsaker i tilfelle angriperen kommer forbi de andre forsvarene. Begrepet minste privilegium gir det en kode med kode som i dette tilfellet vår database skal bare ha tilgang til den informasjonen og ressursene som er nødvendige for det formål.

Når en databasekommando kjører, gjør den det under rettighetene til en brukerkonto. Vi får sikkerhet ved å gi kontoen databasesamtaler kjøres under bare rettighetene til å gjøre ting det normalt trenger å gjøre. Hvis samtalene fra en database bare skal lese data fra et bord, må du bare gi kontovalgsrettighetene til tabellen og ikke sette inn eller slette. Hvis det finnes spesifikke tabeller, trenger det å oppdatere, si en ordre tabell, og deretter gi den inn og oppdater til den tabellen, men ikke andre tabeller som en som inneholder brukerinformasjon.

Avbestillingsordrer kan håndteres via en egen konto som bare brukes på siden som gjør oppgaven. Dette vil forhindre at en ordre fra bordet blir vanskeligere på andre steder. Ved å bruke en egen database-konto for de administrative funksjonene til nettstedet, med de nødvendige større rettighetene enn den offentlige bruk kan gjøre mye for å forhindre skade fra en bruker som finner et åpent injeksjonsangrep.

Dette forhindrer ikke alle angrep. Det ville ikke gjøre noe for å hindre å returnere ekstra resultater som det tidligere eksempelet som viser hele innholdet i et bord. Det ville forhindre angrep fra å oppdatere eller slette data.

Konklusjon

SQL-injeksjon er det farligste angrepet, spesielt når du tar hensyn til hvor sårbare nettsteder er til det og hvor mye potensial denne typen angrep har, forårsaker mye skade. Her har jeg beskrevet SQL-injeksjonsangrep og demonstrert skaden man kan gjøre. Heldigvis er det ikke så vanskelig å beskytte webprosjektene dine fra dette sikkerhetsproblemet ved å følge noen få enkle regler.

Stol aldri på noen eksterne data som er tatt med i søknaden din. Det bør bekreftes mot en hviteliste med gyldige innganger før de behandles videre. Dette kan bety at et heltallsparameter faktisk er et heltall eller en dato er en gyldig datoverdi. Bekreft også tekst for å bare inkludere tegnene parameteren vil trenge. For et tekstsøk kan du ofte bare tillate bokstaver og tall og filtrere ut tegnsetting som kan være problematisk, for eksempel likestilt eller semikolon.

Bruk parameterisering og unngå strengkonsentrasjon når du oppretter SQL-anrop. Lagrede prosedyrer er ikke et paradis, da de kan være sårbare også hvis enkel strengkonsentasjon brukes. Parameterisering unngår mange av problemene med strengkonsentrasjon.

Koden som kommer til databasen, skal kjøres med de minste rettighetene som er nødvendige for å fullføre oppgavene som trengs. I noen tilfeller bør databasesamtaler som brukes av webapplikasjonen, måtte gjøre endringer i databasestrukturen, for eksempel å droppe eller endre tabeller. Du kan legge til et ekstra lag med beskyttelse ved å kjøre separate deler av nettstedet under forskjellige kontoer. Databasekontoen som brukes til vanlige brukerhandlinger, har sannsynligvis ingen grunn til å endre tabellen som inneholder rollene eller brukerens rettigheter. Kjører de administrative delene av nettstedet under en mer privilegert konto og sluttbruker-delen under en mindre privilegert kan gjøre mye for å redusere sjansene for kode som strekker seg gjennom fra å forårsake andre problemer.