Beveiliging
Inhoud
- 1 - Wat is beveiliging?
- 2 - Als het niet specifiek mogelijk moet zijn moet onmogelijk zijn.
- 3 - Gebruik NOOIT onbekende data in een bestandsnaam.
- 4 - Vertrouw niets en niemand, nooit en nergens.
- 5 - Gebruikers laten aanloggen
- 6 - HTTP-Auth
- 7 - PHP's Sessions
- 8 - Uitloggen
- 9 - Uploads
- 10 - Databases
- 11 - Toegangsbeveiliging via .htaccess
- 12 - Mythes rond beveiling
- 13 - Veelgemaakte fouten
Wat is beveiliging?
Beveiliging is het voorkomen dat er iets gebeurt wat je niet wilt.
De grondregels van beveiliging zijn simpel:
- Als het niet specifiek mogelijk moet zijn moet onmogelijk zijn.
- Vertrouw niets en niemand, nooit en nergens.
Als het niet specifiek mogelijk moet zijn moet onmogelijk zijn.
Of simpeler: Als het niet hoeft, dan mag het niet.
- Zorg dat je script alleen datgene kan doen wat jij wilt dat kan doen, en niets anders.
- Als je script geen include vanaf een URL hoeft te kunnen doen, zet die functionaliteit dan uit zodat het ook niet door een hacker gedaan kan worden
- Als je niet in je documentroot hoeft te schrijven, maak hem dan read-only.
Gebruik NOOIT onbekende data in een bestandsnaam.
(denk hier een knalrood kader omheen met flikkerende lichten en sirenes enzo)
Echt gewoon nooit, onder geen enkele voorwaarde. Doe het gewoon niet, nul, nappes, niet, houdt ermee op. Nee, ook niet als je eerst controleert of de tekst een toegestane tekst is wat het is gegarandeerd dat je die conrole niet goed doet of ergens omzeilt.
Gevaar: Als de hacker zelf de naam van een bestand kan bepalen kan hij het ook 'index.php' noemen en is je site weg.
Als de hacker een URL kan injecteren in een include() kan hij zijn eigen code laten uitvoeren en is je site ook weg.
Vertrouw niets en niemand, nooit en nergens.
Alles wat van de client komt is mogelijk gehackt,alles.
- Ook hidden vars in formulieren.
- Ook read-only en disabled velden in formulieren.
- Ook cookies.
- Op shared hosts kan zelfs sessiedata verdacht zijn omdat sommige hosters alle sessions van alle sites in dezelfde /tmp directory laten schrijven.
Alles wat van de browser komt is aan te passen, echt alles. In sommige browsers is het zelfs standaardfunctionaliteit maar iedereen kan, zelfs met PHP, een eigen POST request opstellen.
Gebruikers laten aanloggen
Er zijn twee manieren om gebruikers op een website te laten aanloggen, de HTTP-Auth manier en PHP's sessions.
HTTP-Auth
Voordelen
- Eenvoudig op te zetten
Nadelen:
- niet flexibel
- niet erg veilig
- geen echte nette uitlogoptie
PHP's Sessions
Om mensen aan te laten loggen moet je drie dingen kunnen onthouden:
- Om welke gebruiker gaat het?
- Heeft de gebruiker de juiste gegevens ingevuld om aan te mogen loggen?
- Is de gebruiker waarmee ik nu te maken heb gezelfde die zojuist die gegevens invulde?
Sessions maken het mogelijk dit te doen.
Een session is gebonden aan een browserscherm via een sessionid dat in een cookie wordt verstuurd. Als je iets in een session zet, blijft dat voor die browser behouden. Wanneer iemand aanlogt kun je dus in de sessiondata opslaan dat dat gedaan is.
Opslaan dat iemand is ingelogd is een kwestie van een variabele op 1 zetten. Als iemand niet is aangelogd zet je er 0 in.
Opslaan wie er ingelogd gaat op dezelfde manier, aan de hand van de opgegeven gebruikersnaam kun je bijvoorbeeld het gebruikersid uit een database achterhalen en dat in de session zetten. Je zou dit ook kunnen combineren met de bovenstaande methode en het gebruikersid op nul zetten wanneer de gebruiker niet is ingelogd. Immers, als iemand niet is aangelogd dan weet je niet wie het is.
Om te voorkomen dat de verkeerde persoon de sessie in handen krijgt sla je bij het aanloggen het IP adres van de gebruiker die aanlogt op in de sessie. Als een hacker de sessie probeert over te nemen dan zal zijn IP adres niet meer overeenkomen met het adres dat in de sessie stond.
In dit voorbeeld kun je aanloggen met gebruikersnaam 'pfz' en wachtwoord 'PgSQL'. Als je de juiste gegevens invult wordt de array $_SESSION['inlogdata'] gevuld met een element 'ingelogd', die op 1 staat wanneer de persoon is ingelogd.
De array 'inlogdata' is niet perse nodig, maar het voorkomt dat je deze gegevens gaat verwarren met andere gegevens. Als je bijvoorbeeld in je applicatie ook een variabele 'remote_addr' gebruikt en die op wilt slaan in de session, dan wil je absoluut niet dat je daarvoor per ongeluk de remote-addr van de inlogdata gebruikt.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | <?php
session_start();
if ($_SERVER['REQUEST_METHOD']=='POST' and $_GET['cmd']=='login')
{
if ($_POST['login']=='pfz' and $_POST['wachtwoord']=='PgSQL')
{
$_SESSION['inlogdata']['ingelogd']=1;
$_SESSION['inlogdata']['remote_addr'] = $_SERVER['REMOTE_ADDR'];
}
}
if (isset($_GET['cmd']) and $_GET['cmd']=='logout')
{
$_SESSION['inlogdata']['ingelogd']=0;
$_SESSION['inlogdata']['remote_addr']='';
}
if (isset($_SESSION['inlogdata']['ingelogd']) and isset($_SESSION['inlogdata']['remote_addr']) and $_SESSION['inlogdata']['ingelogd']==1 and $_SESSION['inlogdata']['remote_addr']==$_SERVER['REMOTE_ADDR'])
{
echo 'Gebruiker is ingelogd.<br />';
echo '<a href="?cmd=logout">uitloggen</a>';
}
else
{
// Als de gebruiker wel als ingelogd staat aangemerkt, maar het ip adres niet overeenkomt, dan wordt er mogelijk gehijackt
if ($_SESSION['inlogdata']['ingelogd']==1 and $_SESSION['inlogdata']['remote_addr']!=$_SERVER['REMOTE_ADDR'])
{
echo 'Mogelijke hijack!';
}
echo 'Gebruiker is niet ingelogd.<br />';
echo '<form method="post" action="?cmd=login">
login: <input name="login" type="text"/><br />
wachtwoord: <input name="wachtwoord" type="password"/><br />
<input type="submit" value="inloggen"/>
</form>';
} |
Uitloggen
uitloggen met javascript's onUnload()?
Dit is zuiver theoretisch mogelijk, maar praktisch erg lastig. onUnload() geeft aan dat de huidige pagina wordt verlaten, niet de website. Je zult dus iets moeten bouwen dat kan achterhalen of de volgende pagina die wordt geladen binnen of buiten je site ligt.
Bedenk ook goed of je wel wilt dat mensen direct worden uitgelogd zodra ze ook maar één pagina buiten jouw website bezoeken, want dat doen mensen zeer regelmatig. Denk aan webmail, of gewoon even iets opzoeken met google.
Wat je beter kunt doen is mensen gewoon lekker ingelogd laten en kijken of ze nog actief zijn door bij te houden wanneer ze voor het laatst een pagina hebben bezicht. Als iemand al 15 minuten geen pagina op je site meer heeft opgevraagd dan zal hij gestopt zijn en kan hij worden uitgelogd.
Uploads
Bij het uploaden van een bestand ontvang je in $_FILES een aantal gegevens over het bestand. Een van die gegevens is de naam van het bestand dat is verstuurd. Gebruik die naam nooit als bestandsnaam op de server.
Waarom niet: die naam kan door een hacker gevuld zijn met b.v. index.php, of functies.inc. Als je die naam gebruikt dan schrijft je in principe een script weg. Als de hacker dan ook nog eens PHP upload dan kan hij via een URL dat script aanroepen en ben je gehackt. Dit werkt ook in bijvoorbeeld een images directory: http://jouwsite/images/hackfile.php en je bent gehackt.
Hoe dan wel:
Bepaal altijd zelf een nieuwe naam voor de bestanden die je ontvangt, zoals de huidige datum+tijd, of een sequencegetal uit een database. Gebruik de originele naam gewoon helemaal niet voor het verzinnen van de nieuwe naam, op geen enkele manier. Als je de origele naam nog nodig hebt, sla hem dan ergens op, bijvoorbeeld in een database. Bij het tonen van het bestand kun je de naam dan printen op het scherm, of megeven als filename propery in een download.
Databases
Toegangsbeveiliging via .htaccess
Zie ook: htaccess
Apache heeft zelf een redelijk flexibel systeem om toegang te regelen.
Een veelgebruikte beveiliging is om bepaalde directories alleen toegankelijk te maken voor bepaalde IP adressen (thuis, het kantoor)
Hiertoe maak je een .htaccess bestand in de directory en daarin zet je:
Order deny,allow Allow from w.x.y.z Deny from all
Waar w.x.y.z het ip adres is dat moet worden toegelaten. Meerdere IP adressen zijn toegestaan, deze moeten worden gescheiden met een spatie.
Order bepaald welke eerst moet worden gecontroleerd. Bij deze volgorde weigert hij iedereen, en gecontroleerd hij wie er wel toegang heeft. In theorie zal een verkeerde volgorde iedereen weigeren.
Een IP adres van bepaalde range weigeren/toestaan.
Sommige aanbieders zoals de HTML validator van W3C, gebruiken meer dan één IP adres, nu kan je alle IP adressen die ze gebruiken in gaan voeren... Maar het kan makkelijker.
De HTML validator heeft een eigen IP range, waar alleen het laatste nummer anders is. Dus dit IP-adres 81.70.206.26. zal altijd werken.
Alles wat begint met 81.70.206.26. zal worden geaccepteerd. Het kan zo kort als je zelf wilt, maar let op dat je dan ook kans hebt om meerdere mensen toe te laten!
Voorbeeld:
Order deny,allow Allow from 127.0.0.1 81.70.206.26. Deny from all
Dit zal alleen jouw lokale computer en de validator toelaten. 127.0.0.1 is het zogenaamde LOOPBACK adres waarmee je je eigen computer kan bereiken.
Als je iedereen uit het gehele lokale netwerk toe staan gebruik je dit IP adres. 192.168.
Order deny,allow Allow from 127.0.0.1 81.70.206.26. 192.168. Deny from all
Mythes rond beveiling
- Mythe: Je kunt zien of een request van je eigen site komt middels HTTP_REFERER.
Dit is volkomen onjuist. De waarde van HTTP_REFERER wordt ingesteld door de browser en kan kinderlijk eenvoudig worden vervalst, er zijn zelfs plugins voor o.a. firefox die je specifiek in staat stellen om alle headers die je wilt aan te passen, inclusief de referer. Gebruik dit nooit voor beveiligingsdoeleinden.
Veelgemaakte fouten
- $_REQUEST ipv $_GET, $_POST en $_COOKIE
De superglobal $_REQUEST bevat alle data van $_GET, $_POST en $_COOKIE, waarbij $_COOKIE de waarde van $_GET en $_POST overschrijft. Het gevaar is dat een hacker een cookie kan setten met de naam van een parameter die jij via $_GET of $_POST gebruikt om de bezoeker te laten navigeren, of inloggen of betalen. Zodra dat cookie bestaat werkt die parameter dus niet meer en is je site effectief onbruikbaar voor de bezoeker die dat cookie heeft.
Gebruik dus altijd $_GET als je $_GET bedoelt, $_POST als je $_POST bedoelt en $_COOKIE als je $_COOKIE bedoelt. Nooit $_REQUEST.
Onderwerpen die hier onder vallen:
