[Home/Nieuws] [Magazines] [Meetings] [Downloads] [Redaktie] [Geschiedenis] |
Trojan Eel
Dit document is hoofdzakelijk bedoeld voor mensen die webpagina's maken (vooral
webapplicaties) om ze te wijzen op een paar voor de hand liggende lekken die
kunnen ontstaan als deze niet voldoende voorkomen worden, ook al zullen er vast
ook mensen zijn die er misbruik van willen maken, waarvoor ik uiteraard geen
verantwoordelijkheid neem :) (#include) Dit document is absoluut niet opgezet
als volledige gids van alle mogelijke lekken die er mogelijk zijn via de webserver,
en bevat enkel veel voorkomende fouten. Als je echt bezorgd bent over beveiliging,
houdt dan de advisories van de software die je gebruikt (bv Apache, Perl, PHP)
goed in de gaten, en meldt je aan bij enkele beveiligingsmailinglists. Ik zal
in document uitgaan van het gebruik van Apache, PHP en MySQL aangezien dit veel
gebruikt wordt. Ook als je zelf andere software gebruikt (Perl, of misschien
een andere database) zullen de principes die hier omschreven worden grotendeels
hetzelfde zijn. Nu dit gezegd is, is de tijd daar om te beginnen. <?php php // $user en $password zijn door de gebruiker via formulier of wat dan ook opgegeven $result = mysql_query("SELECT * FROM auth WHERE user='$user' AND password='$password'"); if ($result && mysql_num_rows($result)) { // Login gelukt } else { // Login niet gelukt } ?> Als zowel user en password correct is, zal de query 1 of meerde rows terug sturen (als het goed is maar 1, maar goed), in principe geen slechte manier van authenticatie (al is het misschien beter om de wachtwoorden eerst te versleutelen), maar het gebruikt ongecontroleerde user input! Als speciale tekens niet uitgefilterd worden kan de user bijvoorbeeld intypen: user: root'; SELECT * FROM auth WHERE 0 password: onbelangrijk
SELECT * FROM auth WHERE user='root'; SELECT * FROM auth WHERE 0 AND password='onbelangrijk' Nu is het in (My)SQL mogelijk queries te scheiden met een ;, en voorgaande user input zorgde er dus voor dat de password check wegviel. Nu zou je eventueel de ; kunnen weghalen, maar daar ligt het probleem niet. Kijk maar: user: root; password: bla' OR 1 OR 'bla dit word: SELECT * FROM auth WHERE user='root' AND password='bla' OR 1 OR 'bla' en is dus een query, waar de wachtwoord controle altijd 'waar' is (door de 1) en je dus de beveiliging kan omzeilen. Verder kunnen rare tekens ook SQL errors opleveren, bijvoorbeeld als iemand een ' in een veld zet (zonder bijbedoelingen). Maar goed, dit is uiteraard te voorkomen. Als deze voorbeelden bekeken worden ziet men dat er pas problemen komen zodra de user input uit een waarde gaat, en dus een ' stuurt. Deze is te escapen door een ' of een \ er voor te zetten, bijvoorbeeld door: - gewoon string replacement te doen in user input - mysql_escape_string() gebruiken, als dit beschikbaar is - in PHP de magic_quotes optie te gebruiken al is dit laatste af te raden om dat je dan geen controle meer hebt over welke variabelen wel en niet gequote zijn. Let wel op dat quoting niet dubbel gebeurd, bijvoorbeeld zowel met magic_quotes en string replacements, anders krijg je: ' wordt \' \' wordt \\' en \\ is gewoon een geescapete \, en dus heb je weer een losse ' Hier zie je trouwens een ander teken wat problemen op kan leven: het escape teken \, want behalve dat bijvoorbeeld \n door een enter vervangen wordt, is er ook weer een beveiligingslek, want als je in bovenstaand voorbeeld zet: user: root password: bla\' OR 1 OR \'bla Dan wordt de escaping van ' ongedaan gemaakt, en bestaat het lek weer! Het is zoals je merkt beter om alleen goede chars door te laten, dan om steeds nieuwe foute chars te stoppen, maar als je dit niet doet is het escapen van ' en \ wel het minimum. Een situatie die trouwens in PHP niet voorkomt (PHP decodeert) maar elders wel een rol kan spelen is het gebruik van %xx codes in de URL, om een bepaalde ASCII code mee te sturen. Als dit niet gedecodeerd wordt, kunnen bepaalde lekken ontstaan als het script tekst op tekens controleert en deze tekens geencode zijn. PHP als CGI 1. Het is mogelijk elke file op het systeem te bekijken (http://www.server.nl/cgi-bin/php?/etc/passwd) 2. Het is mogelijk door de webserver beschermde webpagina's te bereiken. Als je een bestand http://www.server.nl/private/index.php hebt, die met .htaccess of op een andere manier door de webserver beschermd wordt, zal de webserver eerst access controleren voordat hij PHP aanroept. Door PHP direct aan te roepen, dus via http://www.server.nl/cgi-bin/php/private/index.php, zal de webserver kijken of je /cgi-bin/php mag bekijken, en dus de andere beveiliging omzeilen. De oplossing is natuurlijk simpel, gewoon niet de binary in een webdirectory zetten :-) . - Specifiek: phpMyAdmin phpMyAdmin (http://phpwizard.net/phpMyAdmin/) wordt vaak gebruikt om via het web databases te beheren. Het script is in principe zeer goed, en heel veilig, op een klein puntje na: Als je basis authenticatie gebruikt (naar de server toe) dan is login/wachtwoord van de database al ingevuld, en iedereen die het script opstart is automatisch ingelogd. Bij uitgebreide authenticatie geef je bij het opstarten een login/password op. De oplossing is, zoals vaak, simpel: Laat de webbrowser de directory afschermen (.htpasswd). Zie eventueel ook PHP als CGI, punt 2... - Foutmeldingen in de browser PHP toont standaard foutmeldingen in de browser, en vaak schrijven mensen code die SQL errors tonen in de browser. Let op dat deze foutmeldingen veel informatie kunnen geven over de structuur van de database/server (bestandsnamen, tabelnamen, veldnamen, etc.). Beter is het foutmeldingen te mailen naar jezelf, met informatie als URL e.d., of deze in een bestand wegschrijven. - Openen/schrijven naar van bestanden Twee punten: 1. Als je bij het openen het bestand door de gebruiker laat beïnvloeden, kan het zijn dat deze een geheel ander bestand opent, bijvoorbeeld door als naam /../../../../../etc/passwd op te geven. Het is belangrijk dat je controleert dat het doelbestand niet buiten een directory is. PHP heeft hier mogelijkheden voor (de doc_root optie, als ik me niet vergis) - Authenticatie/Sessies Voor het doorgeven van gebruiker/wachtwoord gegevens zijn een aantal mogelijkheden. De simpelste is waarschijnlijk HTTP authenticatie, die echter als nadeel heeft dat deze het wachtwoord in plain-text stuurt bij elk request, en dat hiervoor PHP als module moet werken. Een andere methode is het stoppen van user/password in een cookie of de URL. Het nadeel hiervan is dat, zelfs als beiden versleuteld worden, beiden altijd nog eventueel te achterhalen zijn (zie ook artikel betreffende webmail). De beste methode is waarschijnlijk het werken met sessies. De gebruiker logt in, het script controleert de gegevens en als deze kloppen wordt een uniek nummer aan de gebruiker toegewezen. Dit nummer wordt aan de browser als cookie (of in de URLs) gestuurd, en deze zal dit voortaan bij elke pagina mee sturen. De inlog gegevens, in combinatie met de sessie code, worden in een database opgeslagen. Op het moment dat een pagina, met sessie code, wordt opgeroepen worden de inlog gegevens uit de database gehaald, en gecontroleerd. Deze gegevens zijn nu niet voor de gebruiker beschikbaar, en worden ook minder heen en weer gestuurd. Het enige wat verder moet gebeuren is de sessie database op te schonen, door bijvoorbeeld een uur na het laatste bezoek de sessie code te verwijderen, en de gebruiker opnieuw laten inloggen. Hiermee voorkom je ook dat iemand inlogt op een publieke computer, en daarna zomaar ik kan loggen zonder wachtwoorden. Voor extra veiligheid zou in de sessie database eventueel ook nog gegevens
kunnen staan als host en browser en zou een maximale sessie duur kunnen worden
ingesteld, waarna de gebruiker sowieso opnieuw moet inloggen.
|
De informatie in 't Klaphek dient slechts een educatief doel. Gebruik van deze informatie zou strafbaar kunnen zijn. De redaktie wijst iedere verantwoordelijkheid voor gebruik door lezers van de in 't Klaphek opgenomen informatie af. De mening van een auteur weerspiegelt niet noodzakelijkerwijs de mening van de redaktie of uitgever. |