Lesmateriaal Webapplicatie Beveiliging – PHP

Voorwoord

In deze blogpost zal ik verschillende methodes van het uitbuiten van beveiligingslekken beschrijven. Dit is in geen enkele mate toestemming om deze injecties, uitbuitingen en hacks uit te voeren en dient enkel als voorbeeld om het idee van beveiliging te onderbouwen. Om deze reden ga ik niet heel diep in op de hacking onderdelen en meer in op het idee van beveiliging. Deze blogpost is de eerste van een vier-delige reeks aan blogposts over de beveiliging van webapplicaties, vanuit het oog van een docent. Veel leesplezier!

PHP Beveiliging

Inleiding

PHP is een programmeertaal die momenteel het meest wordt gebruikt op websites (79.2%). Het is een taal die nog vaak wordt geupdated en waar veel frameworks omheen zijn gebouwd. Veel van deze frameworks gebruiken dan ook de nieuwste beveiligingstechnieken om de websites zo snel mogelijk te houden. Hieronder ga ik een klein stukje schrijven over wat PHP precies is en en vervolgens schrijf ik wat over het beveiligen van PHP applicaties. Dus wat is PHP? PHP (wat staat voor PHP: Hypertext Processor) is een programmeertaal die is gemaakt om op webservers dynamische webpagina’s te creëren. Via PHP kunnen er bijvoorbeeld onderdelen vanuit een database worden getoond op een website. Ook is PHP goed in het verwerken van data, bijvoorbeeld na het invullen van een formulier. In het SQL voorbeeld had ik het over een login formulier. Hier had ik het ook over een aantal onderdelen die kunnen worden toegepast om SQL meer te beveiligen. Deze onderdelen worden toegepast via PHP. In het onderstaande voorbeeld maak ik eerst een simpel inlogformulier in HTML:

Het formulier bevat een method, die aangeeft in welke vorm de data uit het formulier wordt opgestuurd en een action, naar welk PHP bestand de data wordt verstuurd. In het formulier staan drie inputvelden. Een tekstveld genaamd “gebruikersnaam”, een wachtwoord veld genaamd “wachtwoord” en een verzendknop. De bedoeling is dus dat een gebruiker in dit formulier zijn of haar gebruikersnaam en wachtwoord invult en vervolgens op de verzendknop klikt. De verwerking van die data ziet er als volgt uit in het bestand genaamd “verwerken.php”:

prepare(“SELECT * FROM users WHERE (naam=:naam AND wachtwoord=:wachtwoord)”);
	
	$query>bindParam(“:gebruikersnaam”, $gebruikersnaam);
	$query>bindParam(“:wachtwoord”, $wachtwoord);

	$resultaat = $query->execute();
}
Op de eerste regel wordt PHP zogenaamd “geopend”. Voor het systeem wat de code zal verwerken (bijvoorbeeld de browser) is het nu duidelijk dat het hier gaat om PHP code. In de volgende regel staat een if-statement, een soort check om te kijken of het formulier is ingevuld. Deze regel zegt eigenlijk: “als er op de verzend knop is geklikt”. Vervolgens worden er twee variabelen aangemaakt, $gebruikersnaam en $wachtwoord en gevuld met de bijbehorende data uit het formulier. Daarna wordt er een variabele genaamd $connectie aangemaakt en deze bevat de connectie met de database via PDO. Er wordt een PDO instantie gemaakt, die vraagt om de database host (waar de database staat), database naam en de gebruikersnaam en het wachtwoord van de database gebruiker. Deze connectie wordt vervolgens gebruikt in de volgende regel, waar er een Query wordt klaargezet om naar de database te sturen. Deze Query is vrijwel hetzelfde als het voorbeeld die ik gaf bij het SQL onderdeel. Ik gebruik hier echter niet direct variabelen, maar zogenaamde parameters: :gebruikersnaam en :wachtwoord. Deze parameters “verbind” ik in de volgende regels aan de variabelen, die als waarde de ingevulde gebruikersnaam en wachtwoord bevatten. De uiteindelijke Query bevat dus de gebruikersnaam en het wachtwoord die zijn ingevuld. Bij de laatste regels wordt de Query daadwerkelijk uitgevoerd met een “execute” functie, slaat de output van de uitgevoerde Query op in een variabele genaamd $resultaat en wordt de if-statement weer netjes afgesloten. Met het resultaat kan vervolgens logica worden geschreven om de gebruiker in te loggen met de juiste gegevens. Dit laat zien dat PHP de manier is hoe SQL Queries worden geïmplementeerd in een webapplicatie en worden uitgevoerd. Wanneer we het hebben over SQL beveiliging, wordt dit vrijwel altijd aan de PHP kant uitgevoerd. Dit is wat ik in het volgende kopje wil benoemen, samen met wat algemene tips voor PHP en het configureren van PHP voor bijvoorbeeld Apache of Nginx servers.

Beveiliging

SQL

Onder het kopje SQL zijn er een aantal mooie manieren genoemd waarmee SQL queries kunnen worden beveiligd. De uitvoering hiervan gebeurt grotendeels via PHP. We kunnen een if-statement gebruiken om een conditie te creëren die de code stopzet wanneer de conditie klopt. In de volgende code stoppen we de uitvoer van code wanneer de code de beruchte OR 1=1 bevat.

if (
	strpos($gebruikersnaam, “OR 1=1”) !== false)
	OR strpos($wachtwoord, “OR 1=1”) !== false)
) {
	return false;
}
De if-statement voert twee checks uit, waarvan er maar 1 hoeft te kloppen (via de OR operator). Wanneer de gebruikersnaam OF het wachtwoord de zin OR 1=1 bevat, dan voeren we return false; uit, die de code uitvoer in dit bestand stopzet. Dit is echter een heel specifiek voorbeeld en pakt maar 1 van de vele SQL injectie methodes aan. Escaping is een manier om een groter deel van de injecties aan te pakken. SQL Escaping kan bijvoorbeeld heel simpel worden uitgevoerd op de volgende manier. In plaats van:

$query>bindParam(“:gebruikersnaam”, $gebruikersnaam);
$query>bindParam(“:wachtwoord”, $wachtwoord);
Kunnen we het volgende schrijven om de inhoud van de variabelen $gebruikersnaam en $wachtwoord (de rechtstreekse waardes van de invoervelden uit het formulier) te escapen, ofwel, om de schadelijke symbolen onschadelijk te maken:

$query>bindParam(“:gebruikersnaam”, 
    mysql_real_escape_string($gebruikersnaam));
$query>bindParam(“:wachtwoord”, 
    mysql_real_escape_string($wachtwoord));
In plaats van de waarde van $gebruikersnaam en $wachtwoord direct in de Query te plaatsen, worden ze eerst verwerkt door de functie mysql_real_escape_string(). Deze functie maakt speciale karakters onschadelijk. Ook het zogenaamd “hashen” van wachtwoorden kan worden uitgevoerd middels PHP. Dit husselt het wachtwoord door elkaar om het te beveiligen wanneer wachtwoorden worden vrijgegeven door een injectie of hack. Wachtwoord hashing kan als volgt worden toegepast:
$hash = password_hash($wachtwoord, PASSWORD_DEFAULT);
En om het wachtwoord te verifiëren bij bijvoorbeeld een login check:

if (password_verify($wachtwoord, $hash)) {
	// Gebruiker mag inloggen
} else {
    // Foutief wachtwoord!
}

PHP.ini

Het PHP.ini bestand bevat de configuraties voor de PHP omgeving. Dit bestand wordt vaak niet aangeraakt en bevat een aantal configuraties die kunnen worden ingesteld om de omgeving veiliger te maken. Het eerste voorbeeld hiervan is een veelgemaakte fout, is de optie om foutmeldingen te tonen aan te laten. Wanneer een website wordt ontwikkeld is het wenselijk om foutmeldingen te zien, deze helpen je immers om de oorzaak van een fout op te sporen. Maar, wanneer de website op een productie omgeving draait (ofwel “live”), is dit alles behalve wenselijk. De informatie die een foutmelding toont (dit geldt voor PHP, maar ook SQL foutmeldingen) kan een teken zijn voor hackers om verder te zoeken, maar kan ook waardevolle informatie bevatten van locaties van kwetsbaarheden. Cookies op websites zijn een manier om data te “onthouden” over meerdere pagina-bezoeken of zelfs sessies. Cookies worden daarom vaak gebruikt voor functionaliteiten zoals het “ingelogd blijven”, dit gebeurt vaak via een session ID cookie die de browser sessie bijhoudt. Dit is echter afgeraden (zelfs door PHP.net) en moet op een andere manier worden geïmplementeerd. De optie session.cookie_lifetime verwacht een waarde die aangeeft hoelang een cookie blijft bestaan. Het veiligst is om deze optie op 0 te zetten, zodat de session ID cookie wordt verwijderd wanneer de browser is afgesloten. Op deze manier kan de session ID van een andere gebruiker niet overeenkomen met jouw session ID, waardoor de andere gebruiker misschien wel kan inloggen zonder in te loggen. Een andere optie die standaard niet aan staat, maar wel heel belangrijk is, is strict mode. Deze kan aan worden gezet via session.use_strict_mode=On. Strict mode zorgt ervoor dat er alleen valide session ID’s kunnen worden gebruikt. Verder zijn er nog veel meer opties, rondom bijvoorbeeld het instellen van de hashing methode voor het hashen van wachtwoorden, die hier kunnen worden bekeken: https://www.php.net/manual/en/session.security.ini.php.

phpinfo()

Phpinfo() is een functie die veel informatie van een website / webserver laat zien, informatie die je niet wil tonen aan gebruikers. Zorg daarom ervoor dat je deze info niet zichtbaar maakt voor eindgebruikers op een productie omgeving.

Data Filtering

Bovenstaand, onder het kopje SQL, heb ik al een goed voorbeeld genoemd van het escapen van data voor het in de vorm van een Query naar de database wordt gestuurd. Maar dit valt onder een groter onderdeel genaamd Data Filtering. Een programmeur moet altijd nadenken over de mogelijkheden hoe iemand met kwade bedoelingen ingang kan proberen te krijgen in een website. Data Filtering is een ontzettend belangrijk onderdeel hiervan. In het voorbeeld van SQL sprak ik over het escapen van de ingevulde data in het formulier. Het escapen maakt de speciale symbolen onschadelijk zodat deze geen effect hebben op de Query waar de data in terecht zal komen. Een ander voorbeeld waar data moet worden gefilterd, is een bestandsupload. Wanneer een gebruiker een bestand mag uploaden om bijvoorbeeld zijn of haar profielfoto aan te passen, kan de gebruiker ervoor kiezen om een ander bestandssoort dan een profielfoto up te loaden en zo code uitvoeren waar het niet de bedoeling is. Wanneer jij een bestandsupload maakt zodat gebruikers hun profiel foto kunnen uploaden, betekent niet dat gebruikers deze bestandsupload daadwerkelijk zo gaan gebruiken. Deze specifieke functionaliteit kan op de volgende manier worden misbruikt: Een gebruiker met kwade bedoelingen kan uitzoeken waar de geuploade bestanden terecht komen. De gebruiker upload een afbeelding en zoekt vervolgens (bijvoorbeeld via Element Inspecteren) uit waar de afbeelding terecht komt. De profielfoto is zichtbaar op het profiel, dus de bron van op welke locatie deze foto staat is vaak makkelijk gevonden. In dit voorbeeld gebruiken we het volgende pad: https://website.nl/uploads/profielfoto/foto.png Vervolgens upload de gebruiker in plaats van een afbeelding een PHP bestand met hierin code die bijvoorbeeld de functie phpinfo() uitvoert (zoals hierboven staat). De gebruiker kan dit bestand uitvoeren door in de URL balk naar de URL van het bestand te gaan, op basis van het bovenstaande pad. De gebruiker hoeft dan alleen de bestandsnaam van de afbeelding te vervangen met de bestandsnaam van het PHP bestand: https://website.nl/uploads/profielfoto/hack.php De gebruiker ziet nu in de browser de output van de phpinfo() functie, namelijk behoorlijk wat informatie die een gebruiker normaal niet hoort te zien. De code die de gebruiker kan uitvoeren is eindeloos, dus stel je voor wat voor schade deze gebruiker kan aanbrengen. Door de data te filteren wat een gebruiker in dit voorbeeld upload, kan dit worden voorkomen. In dit geval willen we dat de gebruiker enkel de volgende bestandsextensies kan uploaden: .png, .jpeg, .jpg, .gif Dit kunnen wij als volgt doen, er vanuit gaande dat de waarde van het geuploade bestand in de variabele $profielfoto is geplaatst:

$bestandsExtensie = strtolower(
pathinfo($profielfoto, PATHINFO_EXTENSION));

if (
	$bestandsExtensie == “png”
	OR $bestandsExtensie == “jpeg”
	OR $bestandsExtensie == “jpg”
	OR $bestandsExtensie == “gif”
) {
	// Bestand uploaden
} else {
	// Bestand weigeren
}
Hier verkrijgen we de bestandsextensie door de functie “pathinfo()” en maken we hier voor het gemak kleine letters van middels strtolower(). Vervolgens voeren we een check uit via de condities in de if-statement die checkt of de bestandsextensie png, jpeg, jpg of gif is. Dit is 1 van de voorbeelden hoe een file upload beveiligd kan worden en laat het algemene idee van Data Filtering zien. Het belangrijkste hierin is het logisch nadenken over wat jij wil dat een gebruiker doet, maar ook wat een gebruiker eventueel nog meer kan doen.

Lesvoorbereidingsformulier

Lijkt je het leuk om hier een les in geven maar heb je geen idee hoe je moet beginnen? Dan kun je altijd dit lesvoorbereidingsformulier gebruiken die is gemaakt voor 1e jaars studenten van de MBO opleiding Software Development. Al het geschreven materiaal is Open-Source en vrij om te gebruiken. Lesvoorbereidingsformulier