• PHP Deel 12: Beveiliging

    19 April 2011 00:28 door
    We vinden tegenwoordig zoveel PHP scripts op het internet die onvoldoende beveiligd zijn. Daarom dat we een heel hoofdstuk wijden aan beveiliging. Vertrouw immers nooit op de input van je gebruikers!



    Gegevens verkregen uit $_POST, $_GET, $_REQUEST en $_SERVER kunnen onverwachte inhoud bevatten. Daarom horen we deze te controleren voordat we deze data wegschrijven naar een database, gewoon weg opslaan of gaan gebruiken in onze output. Om hier inzicht in te krijgen geef ik enkele voorbeelden van simpele code die we makkelijk kunnen misbruiken en de oplossingen.

    12.1 Server-side XSS
    Een methode om code uit te voeren op een website en zo zelfs login cookies en dergelijke te kunnen stelen. Om dit te voorkomen laat ik jullie natuurlijk eerst zien hoe het werkt. Door een perfect werkend script te tonen met enkele ernstige beveiligingslekken!

    Mogelijke slachtoffersscript 1
    PHP-code:
    <form action="" method="get">
        <label for="name">Naam</label><input type="text" name="name" id="name" value="" /><input type="submit" value="Verzenden" />
    </form>
    <?php

    if(isset($_GET['name']))
        echo 
    '<p>Je naam is '.$_GET['name'].'</p>';

    ?>
    Zoals je kan zien een super simpel voorbeeld je kan je naam invullen en krijgt vervolgens "Je naam is .." op het scherm. Je zal nu wel denken van niets mis mee, toch een werkend script. Maar zoals je had kunnen verwachten is het geen goed script. Een simpele maar onschadelijke injectie die je hieronder ziet vullen we in het veld naam in.

    Code:
    <script type="text/javascript"> alert("XSS Hack!"); </script>
    Wanneer we dit uitvoeren krijgen we een venster op met het bericht "XSS Hack". Niets speciaals toch? Toch wel we kunnen javascript uitvoeren en zo zouden we cookies kunnen stelen en naar een andere server versturen. Of gewoon loggen wat de gebruiker doet of invult. De mogelijkheden zijn eindeloos. Het is ook mogelijk met $_POST; weliswaar is de methode iets of wat anders maar het principe blijft hetzelfde.

    Nu de oplossing is zeer simpel maar zovelen vergeten het toe te passen. Er zijn meerdere oplossingen mogelijk vandaar 2 correcte oplossingen waarvan ik zelf de 2de vaker uitvoer indien mogelijk.

    Oplossing 1
    PHP-code:
    <form action="" method="get">
        <label for="name">Naam</label><input type="text" name="name" id="name" value="" /><input type="submit" value="Verzenden" />
    </form>
    <?php

    if(isset($_GET['name']))
        echo 
    '<p>Je naam is '.htmlspecialchars($_GET['name']).'</p>';

    ?>
    Oplossing 2
    PHP-code:
    <form action="" method="get">
        <label for="name">Naam</label><input type="text" name="name" id="name" value="" /><input type="submit" value="Verzenden" />
    </form>
    <?php

    if(isset($_GET['name']) && preg_match('/^([a-zA-Z0-9]{2,40})$/'$_GET['name']))
        echo 
    '<p>Je naam is '.htmlspecialchars($_GET['name']).'</p>';

    ?>
    Het is duidelijk dat we bij het 2de voorbeeld nog een stapje verder gaan we beperken de input van de gebruiker tot een reeks tekens. Op deze manier kan er weinig misgaan maar om geen extra verwarring te veroorzaken in je code passen we toch htmlspecialchars toe.

    Onthoud dus: Bij html output waarbij we de gegevens verkregen via de gebruiker maken we gebruik van htmlspecialchars. Ookal komt de data uit een database deze kwam onrechtstreeks ook van de gebruiker en door een lek elders in je script zou deze misbruikt kunnen worden. Vandaar dat we deze even door htmlspecialchars halen, een kleine moeite toch?


    12.2 SQL Injections
    Door het niet controleren van input data kunnen we ook gegevens beinvloeden die uitgelezen worden uit de database om zo te selecteren wat we willen zien.

    Mogelijke slachtoffersscript 1
    PHP-code:
    <?php
    $result 
    mysql_query('SELECT * WHERE password="'.$_POST['pwd'].'" AND username="'.$_POST['username'].'" LIMIT 0,1');
    ?>
    Wanneer we nu bij username het volgende invullen:
    Code:
    " OR 1=1
    Zullen we het eerste resultaat terug krijgen en zal het lijken alsof er niets aan de hand is. Hier dienen we dus duidelijk de data te controleren voor we deze in de database invoegen.

    Oplossing
    PHP-code:
    <?php

    if(isset($_POST['pwd']) && isset($_POST['username']) && preg_match('/^[a-z0-9]{6,14}$/i'$_POST['pwd']) && preg_match('/^[a-z0-9]{6,14}$/i'$_POST['username']))
    {
        
    $result mysql_query('SELECT * FROM users WHERE password="'.mysql_real_escape_string($_POST['pwd']).'" AND username="'.mysql_real_escape_string($_POST['username']).'" LIMIT 0,1');

        
    /**
        * Rest van de code
        */
    }

    ?>
    12.3 HTTP Header Injections
    De headers die met het HTTP protocol worden meegestuurd kunnen beinvloed worden door een gebruiker. Hierdoor kunnen we deze data nooit vertrouwen en moeten we deze ook steeds controleren. De manieren van controleren zijn steeds het zelfde.

    12.4 Conclusie
    Alle gegevens die de bezoeker kan beinvloeden dienen gecontroleerd te worden en mag je nooit vertrouwen. Gebruik de gegevens dus pas wanneer je zeker bent dat ze niets schadelijks kunnen aanrichten.

    Geschreven door: Kurt Aerts.
    Reacties 3 Reacties
    1. Krotec's schermafbeelding
      Krotec -
      Leuke reeks, ik kijk al uit naar de volgende delen!
    1. gbouck's schermafbeelding
      gbouck -
      Kunnen we de volledige reeks op één locatie terugvinden?Geert.
    1. Jelle's schermafbeelding
      Jelle -
      Je vindt een overzicht ervan op http://www.minatica.be/list/category/25-Development