Som du ser är vår webbplats inte anpassad för äldre webbläsare. Vi rekommenderar att du uppgraderar till en nyare webbläsare.
!!

Jobba på CERT-SE? Nu söker vi en förvaltningsledare IT inom cybersäkerhet med sista ansökningsdag 17 augusti, samt en administrativ stjärna med it-säkerhetskunskap till vår desk med sista ansökningsdag 23 augusti.

Uppdaterad | Publicerad

Skydd mot SQL-injektion

Beskriver med kodexempel vad en SQL-injektion är för något, samt hur man skyddar sig på bästa sätt.

Hotet

Under senare tid har SQL-injektionsattacker (eng. "SQL injection attacks") fått mycket uppmärksamhet. Sitic skrev om problemet för cirka en månad sedan, då vi började se automatiserade attacker från botnät. Tyvärr pågår attackerna fortfarande och webbplatser blir fortfarande smittade. Sitic kan inte se någon ökning av attackerna utan snarare att de klingar av. För att hitta exempel på smittade webbplatser kan följande Google-frågor användas:

Varning: ditt system kan bli infekterat ifall du klickar på någon länk i sökresultatet. Många av webbplatserna som finns med i sökresultatet är återställda och är inte längre smittade, men eftersom Google tar tid på sig att indexera om en webbplats inkluderas även "rena" sidor i resultatet. Antalet träffar är alltså missvisande.

Denna artikel beskriver vad en SQL-injektion är för något, och hur man skyddar sig. Den dåliga nyheten är att en SQL-injektion kan ställa till stor skada, exempelvis kan all data i databasen dumpas eller ge en kommandoprompt till angriparen om det vill sig riktigt illa. Den goda nyheten är att sårbarheten är lätt att undvika, men det förutsätter att programmeraren känner till problemet.

Vad är en SQL-injektion?

En webbapplikation är sårbar för SQL-injektion då icke-validerad indata används för att bygga upp en SQL-sats. Antag att parametrarna "username" och "password" inte valideras i metoden "loginUser" (se kodexempel nedan). En användare kan då ange "' or '1'='1" i lösenordsfältet vilket ger följande SQL-sats (indata från lösenordsfältet är markerat i fet stil):

  • select * from user_login where user='hubba' and pwd='' or '1'='1';

Denna SQL-sats returnerar aldrig ett tomt "result set" varför "loginUser" alltid returnerar "true". Angriparen har således kommit förbi autentiseringen. Eftersom SQL är uttrycksfullt är denna typ av sårbarhet allvarlig. Exempelvis kan angriparen utföra CRUD-operationer (Create, Read, Update, Delete), läsa meta-data eller anropa "stored procedures" vilka kan ha kopplingar till operativsystemet. För att skapa en applikationsanvändare i vårt exempel kan följande anges (";" terminerar en SQL-sats och "--" inleder kommentar):

  • select * from user_login where user='hubba' and pwd=''; insert into user_login values ('99', 'lucifer', 'dfgGSba71X_'); --';

    // -- Sårbar Java-kod --
    public boolean loginUser(Connection dbConn, String username, String password) throws SqlException {
    String sql = "select * from user_login where user='" + username + "' and pwd='" + password + "'";
    Statement stmt = dbConn.createStatement();
    try {
    return stmt.executeQuery(sql).next();
    } finally {
    stmt.close();
    }
    }

Lösningen på problemet är att aldrig konkatenera ihop en SQL-sats utan parametrisera den. Det går att göra i de flesta programmeringsspråk. Så här ser det ut i Java:

// -- Säker Java-kod --
public boolean loginUser(Connection dbConn, String username, String password) throws SqlException {
  String sql = "select * from user_login where username=? and password=?";
  PreparedStatement stmt = dbConn.prepareStatement(sql);
  stmt.setString(1, username);
  stmt.setString(2, password);
  try {
    return stmt.executeQuery().next();
  } finally {
    stmt.close();
  }
}

Webbplatser baserade på ASP har varit mest drabbade av automatiserade attacker. Här att ett exempel i ASP:

' -- Sårbar ASP-kod --
' Assume that objConn holds connection to db.
strCmd = "select user_id, user_name from users where user_name='" + strUserName + "' AND password='" + strPassword + "'"
Set objCommand.ActiveConnection = objConn
objCommand.CommandText = strCmd
objCommand.CommandType = adCmdText
Set objRS = objCommand.Execute()
' Process the resultset

Problemet är att SQL-satsen konkateneras ihop med indata, precis som i Java-exemplet. Lösningen är densamma, nämligen att parametrisera SQL-satsen genom att använda "CreateParameter" och "Parameters.Append".

' -- Säker ASP-kod --
' Assume that objConn holds connection to db.
strCmd = "select user_id, user_name from users where user_name=? AND password=?"
Set objCommand.ActiveConnection = objConn
objCommand.CommandText = strCmd
objCommand.CommandType = adCmdText
Set param1 = objCommand.CreateParameter ("user", adWChar, adParamInput, 20)
param1.value = strUserName
objCommand.Parameters.Append param1
Set param2 = objCommand.CreateParameter ("password", adWChar, adParamInput, 50)
param1.value = strPassword
objCommand.Parameters.Append param2
Set objRS = objCommand.Execute()
' Process the resultset

Skydd mot SQL-injektion

Följande kan göras för att skydda sig då applikationen utvecklas:

Använd parametrisera SQL-satser. Bygg aldrig ihop en SQL-sats med indata. Se ovan för kodexempel.

Sanitetskontrollera och validera indata. All indata ska behandlas som ond! Använd ett klassbibliotek för validering av strängfält, datum, heltal, e-postadresser med mera. Viktigt är att valideringen inte sprids ut i koden, utan samlas på ett ställe. JavaScript-validering i klienten kan förhöja användbarheten men går inte att förlita sig på ur ett säkerhetsperspektiv. All indata måste valideras igen på servern eftersom en angripare lätt kan manipulera ett HTTP-anrop. Var restriktiv där det går, undvik exempelvis [<, >, ", #, --, ;, %, ?, @] i ett förnamnsfält. I vissa fall måste "farliga" tecken tillåtas, exempelvis kan citationstecken och "enkelfnutt" förekomma i kommentarsfält.

Anropa enbart "Stored Procedures" (SP). För att öka skiktningen kan alla SQL-satser i applikationen innehålla anrop till en SP istället för exempelvis en "select"-sats. Fördelen är att applikationen arbetar mot ett väl definierat gränssnitt, och det underliggande databasschemat kan ändras utan att gränssnittet och därmed applikationen påverkas. Lösningen ger även säkerhetsmässiga fördelar då det är mer naturlig att parametrisera SP-kod. Observera dock att det ingen garanti, eftersom det även i en SP går att konkatenera ihop SQL-satser. En annan fördel med skiktningen är att all SQL-kod samlas på ett och samma ställe, vilket underlättar kodgranskning.

Kodgranskning. Det finns två typer av kodgranskning:

  • Manuell: En utvecklare går igenom någon annans kod för att hitta buggar och säkerhetshål. Nyttigt och lärorikt om det görs kontinuerligt i utvecklingsteamet. För att leta efter SQL-injektionshål kan koden genomsökas efter förekomster av exempelvis "executeQuery()" (Java), "Execute()" i "ADODB.Command"-klassen (ASP) eller "Open()" i "ADODB.RecordSet"-klassen (ASP). En modern IDE underlättar arbetet, där det går att söka på exempelvis "alla anrop till denna metod" istället för rena textsökningar.
  • Automatiserad: En applikation går igenom källkoden och utför en statisk analys av den. MSCASI (Microsoft Source Code Analyzer for SQL Injection) är ett gratisverktyg som letar efter SQL-injektionsproblem i ASP-kod. För Java finns liknande verktyg, exempelvis Findbugs som är mycket användbar för generell analys. Findbugs är öppen källkod och är även integrerad i en kommersiell produkt som är mer inriktad på att hitta säkerhetsrelaterade buggar.

Automatiserade testverktyg. Denna typ av verktyg letar efter säkerhetshål genom att "spindla" applikationen för att ta reda på potentiellt sårbara sidor, och skickar sedan specialkonstruerade HTTP-anrop till dessa sidor. Microsoft har tillsammans med HP tagit fram verktyget Scrawlr som finns i en nerbantad gratisversion. Sqlmap är ett annat alternativ baserat på öppen källkod. Denna typ av verktyg är inte språkspecifika, exempelvis kan Sqlmap användas för att testa både Java- och ASP-applikationer. Samtidigt är vissa typer av sårbarheter knutna till plattformen, varför vissa testverktyg lämpar sig bättre än andra till en specifik applikation. Verktygen är också olika duktiga på olika typer av SQL-injektion. Att använda flera verktyg kan därför vara en bra lösning.

Logga. Vettig loggning i applikationen kan vara till stor hjälp vid en incident. Loggning förhindrar inte en SQL-injektion, men loggarna kan avslöja vilken del av koden som är sårbar.

Följande kan göras för att skydda en befintlig applikation:

  • Databasanvändare med begränsade privilegier. Webbapplikationen ska använda en egen användare i databasen med så få privilegier som möjligt. Använd aldrig "sa"-användaren eller motsvarande.
  • Applikationsbrandvägg. En applikationsbrandvägg fungerar som ett filter. Om URL:en innehåller en misstänkt SQL-injektion omdirigeras besökaren till en felsida. För Apache finns mod_security. Microsoft erbjuder gratisprogrammet UrlScan. Tyvärr är UrlScan 2.5 för primitiv för att skydda effektivt mot SQL-injektionsattacker eftersom det inte går att filtrera på "query"-delen av URL:en (dvs. allt efter "?"). Den nya versionen, UrlScan v3.0 Beta, är mer kraftfull och tillåter filtrering på "query strings". Tänk på att testa filtret ordentligt innan det tas i produktion. Blockas de URL:er som ska blockas? Håll koll på loggarna eftersom det finns risk att legitim trafik filtreras bort. De finns en rad kommersiella alternativ till UrlScan som ofta innehåller fler funktioner.
  • Övervaka webbserverns loggarna. Använd ett övervakningsprogram, exempelvis OSSEC, för att upptäcka möjliga attacker. Liksom applikationsloggar är webbserverns loggar vara värdefulla vid en incident.
  • En IDS (Intrusion Detection System), till exempel Snort, som lyssnar på trafiken till webbservern på paketnivå och varnar vid potentiella attacker är också ett värdefullt skydd. En IDS upptäcker endast angrepp medan en IPS (Intrusion Prevention System) även kan förhinda angrepp då den blockerar otillåten trafik.

Gunnar Sträng använde både hängslen och livrem, och satte dessutom fast sin plånbok i fickan med en säkerhetsnål. Han visste att god säkerhet bygger på många lager. Förlita dig inte på en metod eller ett verktyg, utan använd alltid flera.

Avslutningsvis vill vi tipsa om WebGoat, vilket är en applikation full av säkerhetshål gjorda med flit. Genom att exprimentera runt i applikationen fås en bra förståelse för hur en SQL-injektion fungerar.

Mer information

http://www.microsoft.com/technet/security/advisory/954462.mspx

http://msdn.microsoft.com/en-us/library/cc676512.aspx

http://blogs.technet.com/swi/archive/2008/06/24/new-tools-to-block-and-eradicate-sql-injection.aspx

http://blogs.technet.com/neilcar/archive/2008/03/15/anatomy-of-a-sql-injection-incident-part-2-meat.aspx

http://www.communities.hp.com/securitysoftware/blogs/spilabs/archive/2008/06/23/finding-sql-injection-with-scrawlr.aspx

http://www.sitic.se/publikationer/namnvart/ms-rad-om-sql-injection/

http://www.sitic.se/publikationer/namnvart/flertalet-svenska-hemsidor-infekterade/