Den seneste tid har jeg rundt omkring i diverse fora og nyhedssites set en masse tråde, flere end hvad jeg husker er normalt og derfor denne artikel, omkring sites hvis udseende eller indhold pludselig er blevet ændret - og i mange tilfælde skælder man ud på fx webhotellet fordi man mener at der her er et sikkerhedshul. I langt de fleste tilfælde skyldes det dog formentlig bare dårlig kode; deres sites er nemlig åben for SQL injections.
SQL Injections er et spørgsmål om, at ens applikation er åben over for, at udefrakommende personer med triste hensigter kan udnytte huller i ens kode til at eksekvere utiltænkte databasekald - hvilket kan føre til usikre loginsystemer eller i værste fald direkte manipulering af data i databasen. Før jeg går videre kan du kigge på dette eksempel;
Det normale
Den mest normale opfattelse er, at SQL injection udelukkende handler om at sikre sine databasekald mod bestemte tegn, der kommer fra brugerinput på en side - har vi fx en (usikker) loginformular til et adminsystemt, hvor der skal angives brugernavn og password kunne vores SQL-sætning se sådan ud i ASP;
SQL = "SELECT id FROM [users] WHERE [username] = '" & Request.Form("txtusername") & "' AND [password] = '" & Request.Form("txtpassword") & "'"
Denne formular ville kunne udnyttes ved fx at skrive
' OR 1=1; -- i txtusername-formfeltet og det ville resultere i dette databasekald;
SELECT id FROM [users] WHERE [username] = '' OR 1=1; --' AND [password] = 'xxx'
I dette tilfælde vil alt efter -- bliver ignoreret, tilbage står kun sammenligningen at username skal være lig '' eller at 1 skal være lig 1 og da det jo er en korrekt sammenligning har vi lige udnyttet muligheden for SQL injection og logget os ind med fri adgang til adminsystemet.
Sikringen af vores loginformular er - i hvert fald ifølge de fleste artikler om emnet - ret lige til da vi bare skal sikre os imod pling (') da det er dette tegn der laver al balladen. Dette kan være gøre vha en simpel replace i vores SQL;
SQL = "SELECT id FROM [users] WHERE [username] = '" & Replace(Request.Form("txtusername"),"'","''") & "' AND [password] = '" & Replace(Request.Form("txtpassword"),"'","''") & "'"
Nu vil forsøget med at indtaste
' OR 1=1; -- i txtusername-formfeltet resultere i en afvisning af loginforsøget (medmindre der da er en bruger der hedder sådan) da vores replace sikrer at plingen i brugernavnet escapes så den rigtige sammenligning sker.
Men vi mangler stadig noget
Det er somregel her de fleste stopper - men så prøv at se nedenstående eksempel. Først har vi vores SQL;
SQL = "SELECT name, description FROM product WHERE id = " & Request.Querystring("id")
Siden hvor ovenstående SQL benyttes kalder vi så vha;
domain.dk/produkt.asp?id=3
Fint nok, nu får vi produkt nummer 3 frem - men hvad sker der så nu;
domain.dk/produkt.asp?id=3;DELETE * FROM product
Nu får vi rigtig nok stadig produkt nummer 3 vist på siden men vores reele databasekald kommer til at se sådan ud;
SELECT name, description FROM product WHERE id = 3;DELETE * FROM product
Hvilket er et tilladt kald og kommer den igennem slettes alle data fra tabellen produkt umiddelbart efter vores select er udført. Ikke godt.
Løsningen
Som det kan ses ud fra ovenstående eksempler er det altså nødvendigt at sikre sig imod SQL injections da det ellers kan få fatale konsekvenser overfor hele databasen. En mulighed er at validere alle data inden de bruges til databasekald - dvs fx sikre sig, at variablen fra ens querystring også virkelig er et tal hvis det skal bruges til at trække data ud fra product-tabellen. Men når nu der findes en bedre løsning, parameters, hvorfor så ikke benytte den? Parametre gør, at man ikke behøver at bekymre sig om SQL injection og selvom det ved første øjekast måske virker lidt mindre tilgængeligt end de sædvanlige simple SQL-statements så er de bestemt til at arbejde med og på sigt kan de endda gøre arbejdet lettere da de giver mere gennemsigtighed over for databasen. Der kunne skrives en hel artikel bare om at arbejde med parametre (og det kommer der måske også), men i første omgang vil jeg bare lægge en muligh løsning for vores to cases ovenover.
Eksempel på login
<%
Set Conn = Server.CreateObject("ADODB.Connection")
Conn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=E:\test\test.mdb"
Set Cmd = Server.CreateObject("ADODB.Command")
Cmd.ActiveConnection = Conn
Cmd.CommandText = "SELECT id FROM [users] WHERE [username] = @username AND [password] = @password"
Cmd.Parameters.Append(Cmd.CreateParameter("@username", 200, 1, 50))
Cmd.Parameters.Append(Cmd.CreateParameter("@password", 200, 1, 50))
Cmd.Parameters("@username") = Request.Form("username")
Cmd.Parameters("@password") = Request.Form("password")
Set login = Cmd.Execute
'Gør noget med login("id")
login.Close
Set login = Nothing
Set Cmd = Nothing
Conn.Close
Set Conn = Nothing
%>
Eksempel på querystring
<%
Set Conn = Server.CreateObject("ADODB.Connection")
Conn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=E:\test\test.mdb"
Set Cmd = Server.CreateObject("ADODB.Command")
Cmd.ActiveConnection = Conn
Cmd.CommandText = "SELECT navn FROM [product] WHERE [id] = @id"
Cmd.Parameters.Append(Cmd.CreateParameter("@id", 3, 1, 50, Request.Querystring("id")))
Set product = Cmd.Execute
If Not product.eof Then
'Gør noget med product("navn")
End If
product.Close
Set product = Nothing
Set Cmd = Nothing
Conn.Close
Set Conn = Nothing
%>
Jeg håber at denne artikel har givet en god idé både omkring hvad SQL injections er samt ikke mindst forslag på løsninger.
fb84368d-2c78-4242-a650-382f75222ce4|2|3.5
ASP, ASP.NET
asp, .net, sql injections