Gaten in CGI scripts

Commando's met gebruikersinput

<?php
echo 'Words found with '.$_GET['query'].' in it:<pre>';
passthru('grep '.$_GET['query'].' /usr/share/dict/words');
echo '</pre>';
?>

Dit script doorzoekt het bestand /usr/share/dict/words met het programma grep op woorden die een string bevatten die door de gebruiker opgegeven kan worden.

Een gebruiker kan echter ook de volgende pagina opvragen:

http://www.example.org/page.html?query=;cat%20/etc/passwd;

In dat geval wordt het volgende uitgevoerd:

grep ;cat /etc/passwd; /usr/share/dict/words

De tekens %20 zijn vervangen door een spatie en een puntkomma is het scheidingsteken van commando's in de meeste shells. Wat er nu gebeurt is dat de gebruiker keurig de inhoud van /etc/passwd op zijn scherm krijgt, informatie die helemaal niet bedoeld was om via deze pagina de rest van de wereld te bereiken. Als shadow-passwords niet zijn ingeschakeld of als de server als root draait heeft de hacker alle informatie die hij nodig heeft voor root toegang tot uw computer.

De oplossing hier is om te controleren of de invoer wel geldig is. Dit kan door middel van reguliere expressies en PHP heeft zelfs een expliciet commando wat hierop controleert (escapeshellarg, escapeshellcmd).

Variabelen manipuleren

<?php
if ($happy_mode=="y") $cmd="cat /var/www/happy";
if ($happy_mode=="n") $cmd="cat /var/www/sad";
...

In PHP wordt de gebruikersinput, ingegeven via GET op POST, gelijk tot een variabele gemaakt (bij een bepaalde configuratie). Bij bovenstaand voorbeeld wordt er vanuit gegaan dat de variabele happy_mode door de gebruiker gegeven wordt, bijvoorbeeld door een formulier waarmee de gebruiker op deze pagina kwam. Natuurlijk kan de gebruiker andere dingen naar dit script sturen. Wat nu als een hacker de volgende pagina opvraagt:

http://www.example.org/happy_script.html?happy_mode=a&cmd=cat%20/etc/passwd

Geen van de bovenstaande if-statements komen overeen met de waarde van happy_mode, en de variabele cmd wordt ingesteld, terwijl deze alleen door het script ingesteld had mogen worden.

U kunt om dit probleem op te lossen de configuratie van PHP aanpassen, waarna u alleen nog van de gebruikersinput kunt genieten door variabelen als $_GET[], $_POST[] en $_COOKIE[] te gebruiken. Zet hiervoor register_globals uit in php.ini. De PHP handleiding heeft een deel over veiligheid, lees het als u PHP gebruikt.

Een vergelijkbare fout in Perl ziet er als volgt uit:

@@params = $query->param();
foreach $param (@params) {
${$param} = $query->param($param);
}

Ook deze code maakt van iedere invoer een variabele. Zo kan een gebruiker iedere willekeurige variabele aanmaken, wat gevaarlijk kan zijn als u niet geheel veilige scripts schrijft.

Verbogen velden

Veel pagina's maken gebruik van verborgen velden om informatie naar de volgende pagina te loodsen in een serie pagina's:

<form action="/scripts/checkout.html">
Credit card number: <input type="text" name="ccnumber">
<input type="hidden" name="price" value="22.50">
<input type="submit">
</form>

Hoe gemakkelijk zou het hier zijn om het artikel voor minder dan 22,50 te bestellen? Wellicht hoeft u alleen het verborgen veld aan te passen.

Een oplossing hiervoor is om de gegevens op de server op te slaan. Sommige scripttalen en webservers ondersteunen sessies, waar u variabelen in op kunt slaan zonder dat de gebruiker ze ooit te zien krijgt. Dit kan echter de applicatie zwaarder maken dan nodig en de gebruiker moet hier waarschijnlijk cookies voor hebben aanstaan. De informatie in cookies stoppen is overigens een slecht idee, want cookies kunnen ook vervalst worden zonder al te veel moeite. U kunt wel de informatie opslaan in bestanden op de server, maar dan moet u wel bijhouden van welke bezoeker welk bestand is.

Een andere methode is om een checksum (bijvoorbeeld een MD5 checksum) te maken van een geheim wachtwoord en de data. Het wachtwoord slaat u op op de server, de data en de checksum stuurt u gewoon door zoals hierboven is getoond. Als er nu met de data gerommeld wordt, is de checksum ongelijk aan de checksum die meegestuurd is en kunt u van een inbraak uitgaan.

Door de gebruiker ingevoerde code

Wat op veel pagina's terugkomt is een forum of een gastenboek. Altijd leuk. Maar wat als een gebruiker HTML code op deze pagina post? Veel webmasters zullen dit toelaten, zodat de gebruikers hun tekst kunnen onderstrepen of vet maken. Toch kan dit ook een veiligheidsgat zijn.

Een gebruiker kan bijvoorbeeld een afbeelding op de achtergrond van uw pagina neerzetten die u daar helemaal niet bedoeld had. Met een afbeelding van enkele MiBi's zal uw pagina snel onbruikbaar worden. Ernstiger wordt het nog als u ook JavaScript, PHP of Perl code op uw pagina toe laat. JavaScript kan leiden tot ernstige problemen met uw pagina, maar PHP of Perl kan uw hele systeem in gevaar brengen.

Een oplossing is om HTML helemaal uit te schakelen. Wilt u dat gebruikers nog wel opmaak kunnen gebruiken, overweeg dan nep-tags zoals [b]bla[/b] te gebruiken en die te parsen tot echte HTML. Schakel alle echte HTML uit en geef niet toe aan het simpelweg vervangen van de vierkante haakjes ([]) in driehoekige haakjes (<>).