Gratis lesje PHP: Veiligheid

Bouw je wel eens een website met PHP? Wil je dat nog gaan doen? Of vraag je je af waarom de ene na de andere database met persoonsgegevens op straat beland? Zoals Dr. House regelmatig zegt: “Everybody lies”! Gebruikers van een webapplicatie, maar vooral hackers en crackers. Als softwareontwikkelaar mag je daarom de gebruiker nooit vertrouwen.

Helaas stammen veel gebruiken bij het schrijven van PHP code nog uit een ver verleden. Toen MySQL enkel nog simpele queries verwerkte en in PHP alles automatisch “veilig” was door het gebruik van “Magic Quotes“. Toen ISO 8859-1 nog de standaard was en Chinese websites zeldzaam waren. PHP beschermde je wel door alle invoer van de gebruiker automatisch te “escapen”, zodat je die veilig in een database query zou kunnen gebruiken. En dat is zoals een beginnend PHP programmeur het nog vaak geleerd krijgt, uit de literatuur, en in het onderwijs. Deze “Magic Quotes” zorgen ook nog regelmatig voor ergernissen. Heb je wel eens polsbandjes besteld met de opdruk “Iemand’s feestje”? Op de volgende pagina staat er ineens “Iemand\’s feestje”, en aan het eind van de rit “Iemand\\\\\’s feestje”. “Magic Quotes” aan het werk. Zo erg zelfs dat veel populaire PHP software begint met het ongedaan maken van dit stukje magie.

Maar waar gaat het dan mis met al die gehackte websites? Een stukje code van een on-line beginnersles PHP en MySQL:

$id=$_GET[‘id’]
$query=” SELECT * FROM contacts WHERE id=’$id'”;
$result=mysql_query($query);

Selecteer alle items uit de tabel “contacts” waar het veld “id” gelijk is aan de variabele $id. Een mogelijke query ziet er dan zo uit:

” SELECT * FROM contacts WHERE id=’1′”

En dat was ook precies de bedoeling. Maar wat gebeurt er als de variabele $id onverwachte tekens bevat? Bijvoorbeeld een enkele quote (‘)? Neem bijvoorbeeld de invoer ‘ OR ”=’ – de query ziet er dan zo uit:

” SELECT * FROM contacts WHERE id=” OR ”=””

Door slechts de URL te wijzigen van http://example.com/index.php?id=1 naar http://example.com/index.php?id=’%20OR%20”%3D’ (de vervalste $id van hier boven, met URL encoding toegepast) heeft de “gebruiker” de database iets laten doen wat helemaal niet de bedoeling was. De PHP “Magic Quotes” zouden hier tegen moeten beschermen, door de quote (‘) te escapen naar \’. Magic quotes staan tegenwoordig echter niet meer standaard aan en de komst van verschillende karaktersets heeft er voor gezorgd dat de escape functies van PHP niet altijd voldoende zijn. Met wat meer inzet kan op deze manier de hele database uitgelezen worden, aangepast worden of verwijderd worden – er is al een poosje software op de “markt” die dit grotendeels automatisch kan.

Er zijn een aantal vrij eenvoudige oplossingen, waarvan enkele de veiligheid garanderen. De meest eenvoudige manier is het gebruik van de mysql_real_escape_string voor alle invoer in de database. Het is echter makkelijk om dat een keer te vergeten; en één keer is te veel. Betere opties zijn het gebruik van de Mysqli extensie of de PDO extensie, in combinatie met “prepared statements”, waarbij de query enkel “placeholders” bevat en de werkelijke data buiten de query om wordt aangeboden. De PHP extensie garandeert dan dat de variabelen op een veilige manier naar de database worden gestuurd. De PDO extensie maakt het bovendien mogelijk om, zonder wijzigingen in de code, een andere database engine te gebruiken.

Genoeg over databases. Je weet nu wat je niet moet doen, en hoe het beter kan. Maar er is meer! Cross site scripting, manipulatie van cookies, en de mogelijkheid voor een gebruiker om bestanden naar keuze te lezen bijvoorbeeld.

“Cross site scripting” wordt meestal mogelijk doordat invoer van de gebruiker, direct of indirect via een database, in de HTML code van de site wordt geplaatst. Neem bijvoorbeeld de volgende code:

<?php
echo ‘<html><body>Hallo, ‘.$_GET[‘naam’].'</body></html>’;

De gebruiker kan zo zijn eigen naam op de website krijgen. Maar kan bijvoorbeeld ook HTML code, of JavaScript code aanbieden, die zonder controle in de HTML uitvoer wordt geplaatst. Deze JavaScript code heeft vervolgens toegang tot onder andere de cookies van de website (en daarmee vaak tot de logingegevens), of kan een gebruiker doorsturen naar een andere site. Dergelijke aanvallen worden vaak toegepast op Facebook en Twitter, om spamberichten te versturen vanaf de account van onschuldige bezoekers. De oplossing ligt over het algemeen in het gebruik van de htmlspecialchars of htmlentities functie. Let op dat de ENT_QUOTES vlag benodigd is om de gegevens geschikt te maken voor in een HTML attribuut (bijvoorbeeld href= bij een hyperlink).

Het bouwen van een inlogsysteem kan ook een uitdaging vormen, terwijl de meest eenvoudige manier over het algemeen redelijk veilig is. Het is onverstandig om cookies direct te gebruiken, omdat deze door de browser aangeleverd worden, en de browser gemanipuleerd kan worden door de gebruiker, of hacker. Een mooi alternatief wordt geboden door de PHP sessions extensie, welke zorgt voor een veilige opslag van de gevoelige gegevens op de server. Realiseer je hierbij wel dat andere sites op de zelfde server mogelijk wel toegang kunnen krijgen tot de gegevens!

Bij het maken van een website die bestaat uit meerdere pagina’s met dezelfde layout, zijn er ook een aantal mogelijkheden. Je kan bijvoorbeeld de layout code (html, head, body tags en menu’s) in iedere pagina opnemen; maar vaak is het makkelijker om de layout code in een apart bestand op te nemen. Met de PHP functie/constructie ‘include‘ of ‘require‘ of ‘readfile‘ kun je de data uit dit bestand opnemen in andere pagina’s. Je kan hierbij eventueel ook nog gebruik maken van functies en PHP code in te template. Een veel gemaakte fout, waar wellicht de beschikbare literatuur ook schuldig aan is, is om via een van de eerder genoemde functies een door de gebruiker opgegeven bestand te openen. Bijvoorbeeld:

<html><body>
<?php
include $_GET[‘page’];
?>
</body></html>

Werkt prima, als de gebruiker netjes http://www.example.com/index.php?page=home.php opvraagt. Maar de gebruiker kan een ander bestand opvragen, bijvoorbeeld /etc/passwd, met alle gebruikersnamen van de server; of het bestand dat je MySQL wachtwoord bevat. Het is soms zelfs mogelijk om een URL op te geven, bijvoorbeeld http://www.example2.com/phpscript.txt. Als deze URL via “include” wordt geopend, heeft de gebruiker, of hacker, vrij spel: hij kan PHP code naar keuze uitvoeren op de server. Het zelfde probleem geldt overigens voor de eval functie. Ofwel: niet doen. En als je het heel graag op deze manier wil bouwen, zorg dan voor een strenge controle van de invoer, door alleen paginanamen toe te staan uit een vaste lijst; bijvoorbeeld met switch/case, if of de in_array functie.

E-mail mogelijkheden op websites bieden vaak mogelijkheden aan spammers. Als een gebruiker het e-mailadres van de ontvanger op mag geven is het al vrij eenvoudig om via de website een ongewenst bericht te versturen. Vaker hebben gebruikers echter de mogelijkheid om een e-mail naar de eigenaar van de website te sturen, waarbij de e-mail verstuurd lijkt te zijn vanaf het adres van de gebruiker. Meestal wordt dit gedaan door een header toe te voegen bij het aanroepen van de mail functie. Maar ook hier geldt dat de gebruiker andere gegevens aan kan bieden dan gewenst; bijvoorbeeld een nieuwe regel gevolgd door een ‘Cc:’ regel, wat er effectief voor zorgt dat de e-mail ook naar een ander adres wordt verstuurd: een nieuwe mogelijkheid om spam te versturen. Daarom is het ook hier van belang om de invoer van de gebruiker goed te controleren, bijvoorbeeld via de filter_var functie.

En nu maar hopen dat alle PHP programmeurs/scripters hier iets van leren, en mijn gegevens in het vervolg wel veilig zijn.

This entry was posted in Computers and tagged , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *