förhindra SQL-injektioner i PHP (och andra sårbarheter)
om du har varit runt webbutveckling ett tag har du nästan säkert hört termen ”SQL-injektion” och några skrämmande historier om det.
PHP, som många andra språk, är inte immun mot denna typ av hot, vilket kan vara mycket farligt. Men lyckligtvis är det något du kan ta konkreta steg mot att skydda dina webbplatser från SQL-injektion och andra liknande hot.
i det här inlägget lär du dig vad SQL-injektion är, vad konsekvenserna av en lyckad attack är, hur en angripare kan dra nytta av sårbar PHP-kod, vad du kan göra för att förhindra det och vilka verktyg du kan använda för att upptäcka de delar av din kod som kan vara föremål för denna typ av hot. Dessutom lär du dig om några andra vanliga sårbarheter som du bör vara medveten om för att skapa säkrare applikationer.
Hur fungerar en SQL-injektion?
det första du behöver veta för att skydda din kod från SQL-injektion är att förstå hur den kan utnyttjas av en angripare.
tanken bakom exploateringen är ganska enkel: en angripare kör skadlig SQL-kod i din databas via din app.
Hur kan någon uppnå det? Genom att missbruka din ansökan ingångar.
Låt oss titta på ett enkelt exempel. Antag att du har ett program som visar en lista med användarnamn, där varje användare har en länk till deras detaljer så här:
och sedan på filen user_details.php
har du det här:
<?php $sql = "SELECT * FROM user WHERE id = ".$_GET;
om användaren fungerar som förväntat och klickar på någon användares namn kommer förfrågningsadressen att se ut så här: http://domain.com/user_details.php?id=8
.
så, vad är fel med det?
värdet på $sql
kommer att vara strängen "SELECT * FROM user WHERE id = 8"
och frågan kommer att fungera bra.
problemet är i den kursiverade delen av denna mening: om användaren fungerar som förväntat och klickar på någon användares namn kommer förfrågningsadressen att se ut så här:
http://domain.com/user_details.php?id=8
.
vad händer om användaren inte agerar som förväntat?
vad händer om någon skulle tillverka en URL som skickar något annat än ett nummer som ID?
tillverkade webbadresser
om webbadressen var något som http://domain.com/user_details.php?id=Peanuts
, skulle den resulterande SQL-strängen vara: "SELECT * FROM user WHERE id = Peanuts"
.
i det här fallet skulle problemet vara för användaren. Frågan skulle helt enkelt misslyckas, och i värsta fall skulle användaren se något slags konstigt meddelande.
men vad händer om webbadressen såg mer ut så här:
http://domain.com/user_details.php?id=1+OR+1%3B
? (Detta är resultatet av urlencode('1 OR 1;')
, förresten.)
i detta fall skulle den resulterande SQL vara "SELECT * FROM user WHERE id = 1 OR 1;"
. Det betyder att resultatet av frågan skulle vara varje användare i databasen istället för bara användaren vars ID är lika med 1. AJ.
men det är fortfarande inte så illa, eller hur?
låt oss nu prova ett mer extremt exempel.
låt oss anta att webbadressen var något liknande
http://domain.com/user_details.php?id=1%27%3B+DROP+TABLE+users%3B--+
, vilket översätts till id=1; DROP TABLE users;--
, vilket effektivt producerar detta SQL-kommando:
"SELECT * FROM users WHERE id = 1; DROP TABLE users;-- "
. Och om du kör den här frågan … stor aj.
om du inte är 100% bekant med SQL-syntax, skulle DROP TABLE
helt radera tabellen. Det skulle vara som om det aldrig fanns.
naturligtvis skulle en ny backup hjälpa till att minska skadan, men fortfarande. Det här är dåligt.
om du vill lära dig mer om denna typ av attack finns det ett bra inlägg här.
men jag tror att det är tillräckligt dåliga nyheter. Låt oss gå vidare till en mer glad plats: hur du kan förhindra dessa typer av SQL-injektioner i dina PHP-appar.
hur man förhindrar SQL-injektioner i PHP
som du såg är problemet när en angripare har möjlighet att köra SQL-kod utan att du är medveten om det.
denna situation presenterar sig som en följd av att frågor skapas i farten via strängsammanfogning och inkluderar data som samlats in från externa ingångar (vanligtvis användarinmatning, men också andra externa källor som filer, svar på API-samtal och så vidare).
så för att förhindra denna sårbarhet i din kod måste du fixa källorna till potentiella problem.
validera dina ingångar
ett enkelt sätt att åtgärda sårbarheten i ovanstående exempel skulle vara att kontrollera om ID-parametern är vad som faktiskt förväntas av det (dvs., ett positivt heltal):
ett annat sätt att göra denna typ av validering är att utnyttja PHP: s inbyggda filter:
<?php
$id = filter_input( INPUT_GET, 'id', FILTER_VALIDATE_INT);
så genom att validera dina ingångar hindrar du angripare från att köra skadlig kod tillsammans med din egen.
det finns inget enkelt sätt att hindra dem från att försöka, men så länge deras försök inte lyckas är du bra att gå.
använd förberedda uttalanden
ett annat sätt du kan skydda din kod mot SQL-injektioner är att använda förberedda uttalanden. Förberedda uttalanden är förkompilerade SQL-kommandon.
de kan användas med ett specifikt databasåtkomstbibliotek (t.ex. mysqli) eller med det mer generiska biblioteket PDO.
Låt oss ta en titt på ett exempel med mysqli:
om du försöker den här koden (ersätter databasuppgifterna, förstås) ser du att om du nås via en juridisk URL som http://domain.com/user_details.php?id=1
får du samma resultat som någon av de skadliga webbadresserna ovan (vilket är informationen om användare som är associerad med ID 1 och inget annat).
om du föredrar att använda PDO kommer koden att se mer ut så här:
inte riktigt mycket skillnad, eller hur?
Sammanfattningsvis erbjuder förberedda uttalanden ett bra sätt att förhindra SQL-injektioner. Dessutom fungerar de vanligtvis bättre än On-The-fly SQL.
nu när du känner till verktyget behöver du bara använda det.
hur man upptäcker PHP-kod som är sårbar för SQL-injektioner
när sårbarheten har hittats är det inte riktigt komplicerat att fixa det. Men hur hittar du dessa typer av sårbarheter i din kod i första hand?
Tja, om du har tur och din kodbas är tillräckligt liten, kommer en enkel recension av koden att göra.
men om din ansökan är medelstor till stor behöver du extra hjälp.
en mycket populär (och öppen källkod) verktyg som du kan använda för detta ändamål är sqlmap, en enkel Python-skript som kommer att försöka attackera någon URL du mata till det och rapportera tillbaka på sina resultat.
här är ett exempel på en körning mot en PHP-baserad webbplats för mig:
ett annat verktyg med öppen källkod du kan använda är Arachni, ett mer sofistikerat verktyg som innehåller en webb-GUI och skrevs i Ruby.
här är en provutgång från att köra den genom CLI:
utöver kodgranskning och testning bör du överväga att använda en RASP-lösning, till exempel Sqreen, för att övervaka dina produktionsmiljöer för att stoppa genomförandet av alla exploater som gör det genom gransknings-och teststadierna.
vilka andra sårbarheter bör du vara medveten om?
Kodinjektioner
SQL-injektion är bara en medlem av en större familj av sårbarheter: kodinjektion.
tanken bakom olika kodinjektionstekniker är alltid densamma. Det handlar om att lura en oskyldig applikation till att köra skadlig kod.
detta kan göras på flera sätt.
i PHP finns det funktioner som är utformade för att helt enkelt utfärda kommandon direkt in i operativsystemet, till exempel funktionen exec
.
här är ett enkelt exempel på hur detta kan utnyttjas:
<?php
exec( 'cp uploads/'.$_GET.' /secure/');
om någon skulle begära http://domain.com/exec.php?file=a.txt%3B+rm+-Rf+%2A+%23
skulle resultatet bli utförandet av detta kommando:
cp uploads/a.txt; rm -Rf * # /secure/
och detta kan utplåna hela disken på servern-ogiltig ingång slår igen!
cross-site scripting
en annan mycket vanlig sårbarhet är cross-site scripting (XSS). I det här fallet är offret användaren som besöker din webbplats.
XSS är när en angripare injicerar skadlig JavaScript-kod i en vanlig HTML-form, som senare kommer att återges av en annan användares webbläsare.
titta på detta PHP-skript:
<?php
echo "<html><p>Hello {$_GET}!</p></html>";
det är uppenbart att avsikten här är att skapa en enkel sida som hälsar användaren.
men när onda kommer till det, vad hindrar dem från att begära
http://domain.com/greet.php?name=Mauro%3Cscript+language%3D%22javascript%22%3Ealert%28%22You+are+hacked%21%22%29%3B%3C%2Fscript%3E
? Denna begäran kommer att producera följande HTML som utdata:
<html><p>Hello Mauro<script language="javascript">alert("You are hacked!");</script></p></html>
detta är helt klart ett mycket naivt utnyttjande-trots allt, vad är skadan i att visa någon en enkel ” du är hackad!”meddelande? Men saker kan bli ganska allvarliga med lite fantasi. Tänk bara på att varje begäran till en server innehåller sessionscookien, så den här sårbarheten kan vara den skyldige bakom dina användares sessioner som kapas.
sedan igen, medan problemen ser fula ut, är korrigeringarna väldigt enkla: det handlar om validering och sanering.
i fallet med XSS, ett enkelt samtal till htmlentities
innan producera den faktiska produktionen kommer att göra.
Fixing vs. upptäcka säkerhetsproblem
som du såg i det här inlägget är det inte den största utmaningen att fixa säkerhetsproblem—både SQL—injektioner och andra typer. Att inse att din kod är sårbar och var den kan utnyttjas är. Du har ett par alternativ här:
- har mycket stark kodning disciplin
- testa dina program noggrant för säkerhetsfrågor
- hävstångs övervakning och skyddsverktyg för att förhindra bedrifter och få varningar i tid
vilken du väljer är upp till dig. Det viktiga är att uppmärksamma dessa problem och agera på den kunskapen. Håll dig säker.