SQL-injektion ( engelsk SQL-injection /SQli ) er en af de mest almindelige måder at hacke websteder og programmer på, der arbejder med databaser , baseret på introduktionen af vilkårlig SQL -kode i en forespørgsel .
SQL-injektion, afhængigt af den anvendte type DBMS og betingelserne for injektion, kan gøre det muligt for en angriber at udføre en vilkårlig forespørgsel til databasen ( for eksempel læse indholdet af alle tabeller , slette, ændre eller tilføje data ), få muligheden at læse og/eller skrive lokale filer og udføre vilkårlige kommandoer på den angrebne server.
Et angreb af typen SQL-injektion kan være muligt på grund af forkert behandling af inputdata, der bruges i SQL-forespørgsler.
En databaseapplikationsudvikler bør være opmærksom på sådanne sårbarheder og tage skridt til at imødegå SQL-injektion.
Der er tre hovedklasser af angreb baseret på SQL-injektion:
Lad os sige, at serversoftwaren , efter at have modtaget id-inputparameteren, bruger den til at oprette en SQL-forespørgsel. Overvej følgende PHP -script:
$id = $_REQUEST [ 'id' ]; $res = mysqli_query ( "VÆLG * FRA nyheder WHERE id_news = " . $id );Hvis en id-parameter lig med 5 sendes til serveren (for eksempel: http://example.org/script.php?id=5 ), vil følgende SQL - forespørgsel blive udført:
VÆLG * FRA nyheder WHERE id_news = 5Men hvis en angriber sender strengen -1 ELLER 1=1 som id-parameteren (f.eks. sådan: http://example.org/script.php?id=-1+OR+1=1 ), så anmodning vil blive udført:
VÆLG * FRA nyheder HVOR id_news = - 1 ELLER 1 = 1Ændring af inputparametrene ved at tilføje SQL-sprogkonstruktioner til dem forårsager således en ændring i SQL-forespørgselsudførelseslogikken (i dette eksempel vil alle nyheder i databasen blive valgt i stedet for nyheder med en given identifikator, da udtrykket 1=1 er altid sand - beregninger udføres ved hjælp af den korteste kontur i diagrammet ).
Antag, at serversoftwaren, efter at have modtaget en anmodning om at søge efter data i nyhederne med parameteren search_text, bruger den i følgende SQL-forespørgsel (her er parametrene escaped med anførselstegn):
$search_text = $_REQUEST [ 'søgetekst' ]; $res = mysqli_query ( "SELECT id_news, news_date, news_caption, news_text, news_id_author FROM news WHERE news_caption LIKE('% $search_text %')" );Ved at lave en forespørgsel som http://example.org/script.php?search_text=Test , får vi følgende SQL-forespørgsel, der skal udføres:
SELECT id_news , news_date , news_caption , news_text , news_id_author FROM news WHERE news_caption LIKE ( '%Test%' )Men ved at indlejre et anførselstegn (som bruges i forespørgslen) i parameteren search_text, kan vi drastisk ændre adfærden for SQL-forespørgslen. For eksempel, ved at overføre værdien ' )+and+(news_id_author='1 ) som search_text-parameteren kalder vi forespørgslen, der skal udføres:
SELECT id_news , news_date , news_caption , news_text , news_id_author FROM news WHERE news_caption LIKE ( '%' ) og ( news_id_author = '1%' )SQL-sproget giver dig mulighed for at kombinere resultaterne af flere forespørgsler ved hjælp af UNION -operatoren . Dette giver en angriber mulighed for at få uautoriseret adgang til data.
Lad os overveje nyhedsvisningsscriptet ( identifikationen af de nyheder, der skal vises, sendes i id-parameteren ):
$res = mysqli_query ( "SELECT id_news, header, body, author FROM news WHERE id_news = " . $_REQUEST [ 'id' ]);Hvis en angriber sender -1 UNION SELECT 4 brugernavn, adgangskode,1 FROM admin som id-parameteren , vil dette få SQL-forespørgslen til at blive udført
SELECT id_news , header , body , author FROM news WHERE id_news = - 1 UNION SELECT 1 , brugernavn , adgangskode , 1 FROM adminDa nyheder med identifikator -1 bestemt ikke eksisterer, vil der ikke blive valgt poster fra nyhedstabellen, men resultatet vil inkludere poster, der er ulovligt valgt fra admin-tabellen som følge af SQL-injektion.
I nogle tilfælde kan en hacker angribe, men kan ikke se mere end én kolonne. I tilfælde af MySQL kan en angriber bruge funktionen:
gruppe_konkat ( col , symbol , col )som kombinerer flere kolonner til én. For eksempel vil funktionskaldet for eksemplet ovenfor være:
- 1 UNION SELECT group_concat ( brugernavn , 0 x3a , adgangskode ) FRA adminOfte har SQL-forespørgslen, der er påvirket af denne sårbarhed, en struktur, der gør det vanskeligt eller umuligt at bruge union. For eksempel script:
$res = mysqli_query ( "VÆLG forfatter FRA nyheder WHERE id=" . $_REQUEST [ 'id' ] . " OG forfatter LIKE ('a%')" );viser kun nyhedsforfatterens navn med det beståede id-id, hvis navnet starter med bogstavet a, og kodeindsprøjtning ved hjælp af UNION-operatøren er vanskelig.
I sådanne tilfælde bruger angribere metoden til at undslippe en del af anmodningen ved hjælp af kommentartegn ( /* eller -- afhængigt af typen af DBMS).
I dette eksempel kan en angriber videregive id-parameteren med værdien -1 UNION SELECT password FROM admin/* til scriptet og dermed udføre forespørgslen
VÆLG forfatter FRA nyheder WHERE id =- 1 UNION VÆLG adgangskode FRA admin /* OG forfatter LIKE ('a%')hvor en del af forespørgslen ( OG forfatter LIKE ('a%') ) er markeret som en kommentar og ikke påvirker udførelsen.
Symbolet; bruges til at adskille kommandoer i SQL-sproget ; ( semikolon ) ved at indlejre dette tegn i en forespørgsel, er en angriber i stand til at udføre flere kommandoer i en enkelt forespørgsel, men ikke alle SQL-dialekter understøtter denne funktion.
For eksempel, hvis i script-parametrene
$id = $_REQUEST [ 'id' ]; $res = mysqli_query ( "VÆLG * FRA nyheder WHERE id_news = $id " );angriberen sender en konstruktion, der indeholder et semikolon, for eksempel 12;INSERT INTO admin (brugernavn, adgangskode) VALUES ('HaCkEr', 'foo'); derefter vil 2 kommandoer blive udført i én forespørgsel
VÆLG * FRA nyheder WHERE id_news = 12 ; INSERT INTO admin ( brugernavn , adgangskode ) VÆRDIER ( 'HackEr' , 'foo' );og en uautoriseret HackEr-post vil blive tilføjet til admin-tabellen.
På dette stadium undersøger angriberen serverscripts adfærd, når han manipulerer inputparametre for at opdage deres unormale adfærd. Manipulation sker med alle mulige parametre:
Som regel kommer manipulation ned til at erstatte et enkelt (sjældent dobbelt eller tilbage) citat i karakterparametrene.
Unormal adfærd er enhver adfærd, hvor siderne, der hentes før og efter citatsubstitutionen, er forskellige (og ikke viser siden med ugyldigt parameterformat).
De mest almindelige eksempler på unormal adfærd er:
osv. Man skal huske på, at der er tilfælde, hvor fejlmeddelelser på grund af sidemarkeringens specifikationer ikke er synlige i browseren, selvom de er til stede i dens HTML-kode.
Design | Kommenterer resten af linjen | Hent version | Sammenkædning af strenge |
---|---|---|---|
MySQL | -- ..., /* ..., eller# ... | version() | concat (string1, string2) |
MS SQL | -- ... | @@version | string1 + string2 |
Oracle | -- ...eller/* ... | select banner from v$version |
string1 || string2 ellerconcat (string1, string2) |
MS Access | Injicere en NULL-byte i en anmodning:%00... | ||
PostgreSQL | -- ... | SELECT version() | string1 || string2,CONCAT('a','b') |
Sybase | -- ... | @@version | string1 + string2 |
IBM DB2 | -- ... | select versionnumber from sysibm.sysversions | string1 || string2ellerstring1 concat string2 |
Ingres | -- ... | dbmsinfo('_version') | string1 || string2 |
For at beskytte mod denne type angreb er det nødvendigt at omhyggeligt filtrere inputparametrene, hvis værdier vil blive brugt til at bygge SQL-forespørgslen.
Lad os antage, at koden, der genererer anmodningen (i Pascal -programmeringssproget ) ser sådan ud:
statement := 'SELECT * FROM users WHERE name = "' + brugernavn + '";' ;For at lave kodeinjektion (lukning af en streng, der starter med et citat med et andet citat, før det slutter med det aktuelle afsluttende citat for at opdele forespørgslen i to dele) var det umuligt, for nogle DBMS , inklusive MySQL , er det påkrævet at citere alle strengparametre . I selve parameteren skal du erstatte anførselstegnene med \", apostrof med \', omvendt skråstreg med \\ (dette kaldes " escaped special characters "). Dette kan gøres med følgende kode:
statement := 'SELECT * FROM users WHERE name = ' + QuoteParam ( brugernavn ) + ';' ; funktion QuoteParam ( s : streng ) : streng ; { ved input - en streng; outputtet er en streng i anførselstegn og med specialtegn erstattet } var i : heltal ; dest : streng _ start Dest := '"' ; for i := 1 til længde ( r ) gør tilfælde s [ i ] af ' '' ' : Dest := Dest + '\ '' ' ; '"' : Dest := Dest + '\"' ; '\' : Dest := Dest + '\\' ; else Dest := Dest + s [ i ] ; end ; QuoteParam := Dest + '"' ; ende ;For PHP kan filtrering være sådan:
$query = "VÆLG * FRA brugere WHERE user='" . mysqli_real_escape_string ( $user ) . "';" ;Lad os tage en anden forespørgsel:
statement := 'SELECT * FROM users WHERE id = ' + id + ';' ;I dette tilfælde har feltet iden numerisk type, og det er oftest ikke citeret. Derfor virker det ikke at "citere" og erstatte specialtegn med escape-sekvenser. I dette tilfælde hjælper typekontrol; hvis variablen idikke er et tal, bør forespørgslen slet ikke køre.
For eksempel i Delphi hjælper følgende kode med at modvirke sådanne injektioner:
hvis TryStrToInt ( id , id_int ) then statement := Format ( 'SELECT * FROM users WHERE id =%0:d;' , [ id_int ]) ;For PHP vil denne metode se sådan ud:
$query = 'VÆLG * FRA brugere WHERE id = ' . ( int ) $id ;For at foretage ændringer i logikken i at udføre en SQL-forespørgsel kræves indsprøjtning af tilstrækkeligt lange strenge. Så minimumslængden af den indlejrede streng i ovenstående eksempler er 8 tegn (" 1 ELLER 1=1 "). Hvis den maksimale længde af en gyldig parameterværdi er lille, kan en af beskyttelsesmetoderne være den maksimale trunkering af inputparameterværdier.
For eksempel, hvis det er kendt, at feltet idi ovenstående eksempler ikke kan have værdier på mere end 9999, kan du "skære de ekstra" tegn fra og ikke efterlade mere end fire:
statement := 'SELECT * FROM users WHERE id = ' + LeftStr ( id , 4 ) + ';' ;Mange databaseservere understøtter muligheden for at sende parametriserede forespørgsler (forberedte udsagn). I dette tilfælde sendes parametre af ekstern oprindelse til serveren separat fra selve anmodningen, eller de undslippes automatisk af klientbiblioteket. Til dette bruges de
For eksempel
var sql , param : streng start sql := 'vælg :tekst som værdi fra dual' ; param := 'alpha' ; Forespørgsel 1 . SQL . Tekst : = sql Forespørgsel 1 . ParamByName ( 'tekst' ) . AsString := param ; Forespørgsel 1 . åben ; ShowMessage ( Query1 [ 'værdi' ]) ; ende ;