Beveiliging is een hot topic. Zorgen dat uw websites veilig zijn, is uiterst belangrijk voor elke webtoepassing. Sterker nog, ik besteed 70% van mijn tijd aan het beveiligen van mijn applicaties. Een van de belangrijkste dingen die we moeten beveiligen, zijn formulieren. Vandaag gaan we een methode bekijken om XSS (Cross-site scripting) en Cross-site request vervalsing op formulieren te voorkomen.
POST-gegevens kunnen van de ene website naar de andere worden verzonden. Waarom is dit slecht? Een eenvoudig scenario ...
Een gebruiker die op uw website is ingelogd, bezoekt tijdens zijn sessie een andere website. Deze website kan POST-gegevens naar uw website verzenden, bijvoorbeeld met AJAX. Omdat de gebruiker is ingelogd op uw site, kan de andere website ook postgegevens verzenden naar beveiligde formulieren die alleen toegankelijk zijn na een login.
We moeten ook onze pagina's beschermen tegen aanvallen met cURL
Met formulesleutels! We voegen een speciale hash (een formuliersleutel) toe aan elk formulier om ervoor te zorgen dat de gegevens alleen worden verwerkt wanneer deze van uw website zijn verzonden. Na het verzenden van een formulier, valideert ons PHP-script de ingediende formulesleutel tegen de formulesleutel die we in een sessie hebben ingesteld.
Eerst hebben we een eenvoudig formulier nodig voor demonstratiedoeleinden. Een van de belangrijkste vormen die we moeten beveiligen, is het aanmeldingsformulier. Het aanmeldingsformulier is kwetsbaar voor brute force-aanvallen. Maak een nieuw bestand en sla het op als index.php in uw web-root. Voeg de volgende code toe binnen het lichaam:
Formulieren beveiligen met behulp van formulesleutels
Nu hebben we een eenvoudige XHTML-pagina met een inlogformulier. Als u formuliertoetsen op uw website wilt gebruiken, kunt u het bovenstaande script vervangen door uw eigen inlogpagina. Laten we nu doorgaan naar de echte actie.
We gaan een PHP-klasse maken voor onze formulesleutels. Omdat elke pagina alleen kan bevatten een vorm sleutel, kunnen we een singleton van onze klas maken om ervoor te zorgen dat onze klasse correct wordt gebruikt. Omdat het maken van singletons een meer geavanceerd OOP-onderwerp is, slaan we dat deel over. Maak een nieuw bestand met de naam formkey.class.php en plaats het in uw web-root. Nu moeten we nadenken over de functies die we nodig hebben. Ten eerste hebben we een functie nodig om een sleutel voor formulieren te genereren, zodat we deze in onze vorm kunnen plaatsen. Plaats in uw PHP-bestand de volgende code:
Hierboven zie je een klasse met drie delen: twee variabelen en een functie. We maken de functie privé omdat deze functie alleen door ons zal worden gebruikt uitgangfuncties die we later zullen creëren. In de twee variabelen bewaren we de formuliertoetsen. Deze zijn ook privé omdat ze alleen kunnen worden gebruikt door functies binnen onze klas.
Nu moeten we een manier bedenken om onze sleutel voor formulieren te genereren. Omdat onze formulesleutel uniek moet zijn (anders hebben we geen beveiliging), gebruiken we een combinatie van het IP-adres van de gebruiker om de sleutel aan een gebruiker te binden, mt_rand () om het uniek te maken en de uniqid () -functie om het nog unieker te maken. We coderen deze informatie ook met md5 () om een unieke hash te maken die we vervolgens in onze pagina's kunnen invoegen. Omdat we md5 () hebben gebruikt, kan een gebruiker niet zien wat we hebben gebruikt om de sleutel te genereren. De hele functie:
// Functie voor het genereren van de sleutel van het formulier persoonlijke functie generateKey () // Download het IP-adres van de gebruiker $ ip = $ _SERVER ['REMOTE_ADDR']; // We gebruiken mt_rand () in plaats van rand () omdat het beter is voor het genereren van willekeurige getallen. // We gebruiken 'true' om een langere reeks te krijgen. // Zie http://www.php.net/mt_rand voor een nauwkeurige beschrijving van de functie en meer voorbeelden. $ uniqid = uniqid (mt_rand (), true); // Retourneer de hash-return md5 ($ ip. $ Uniqid);
Plaats de bovenstaande code in uw formkey.class.php het dossier. Vervang de functie door de nieuwe functie.
Voor deze stap maken we een nieuwe functie die een verborgen HTML-veld uitvoert met onze formulesleutel. De functie bestaat uit drie stappen:
We noemen onze functie outputKey () en maak het openbaar, omdat we het buiten onze klas moeten gebruiken. Onze functie zal de privé-functie oproepen generateKey () om een nieuwe formulesleutel te genereren en deze lokaal in een sessie op te slaan. Ten slotte maken we de XHTML-code. Voeg nu de volgende code toe in onze PHP-klasse:
// Functie voor het uitvoeren van de formuliersleutel public function outputKey () // Genereer de sleutel en sla deze op in de klasse $ this-> formKey = $ this-> generateKey (); // Sla de sleutel op in de sessie $ _SESSION ['form_key'] = $ this-> formKey; // Geef de echo van de formuliersleutel af "formKey. "" /> ";
Nu gaan we de formulesleutel toevoegen aan ons inlogformulier om het te beveiligen. We moeten de klas opnemen in onze index.php het dossier. We moeten ook de sessie starten omdat onze klas sessies gebruikt om de gegenereerde sleutel op te slaan. Hiervoor voegen we de volgende code toe boven het doctype en head-tag:
De bovenstaande code spreekt voor zich. We starten de sessie (omdat we de sleutel van het formulier opslaan) en laden het PHP-klassenbestand. Daarna starten we de les met nieuwe formKey (), dit zal onze klas creëren en opslaan $ formkey. Nu hoeven we ons formulier alleen maar te bewerken, zodat het de formulesleutel bevat:
En dat is alles! Omdat we de functie hebben gemaakt outputKey (), we hoeven het alleen maar in onze vorm op te nemen. We kunnen formuliertoetsen in elke vorm gebruiken door ze gewoon toe te voegen outputKey (); ?> Bekijk nu de bron van uw webpagina en u kunt zien dat er een formulesleutel aan het formulier is gekoppeld. De enige resterende stap is om verzoeken te valideren.
We zullen het hele formulier niet valideren; alleen de sleutel van het formulier. Validatie van het formulier is basis-PHP en tutorials zijn overal op internet te vinden. Laten we de sleutel van het formulier valideren. Omdat onze "generateKey" -functie de sessiewaarde overschrijft, voegen we een constructor toe aan onze PHP-klasse. Een constructor wordt gebeld als onze klasse is gemaakt (of geconstrueerd). De constructor slaat de vorige sleutel in de klas op voor we creëren een nieuwe; dus we hebben altijd de sleutel van de vorige vorm voor het valideren van ons formulier. Als we dit niet deden, zouden we de sleutel van het formulier niet kunnen valideren. Voeg de volgende PHP-functie toe aan uw klas:
// De constructor slaat de formulesleutel op (indien aanwezig) in onze klassenvariabele. function __construct () // We hebben de vorige sleutel nodig zodat we deze opslaan als (isset ($ _ SESSION ['form_key'])) $ this-> old_formKey = $ _SESSION ['form_key'];
Een constructor moet altijd een naam krijgen __construct (). Wanneer de constructor wordt opgeroepen, controleren we of een sessie is ingesteld en zo ja, slaan we deze lokaal op in onze old_formKey veranderlijk.
Nu kunnen we onze formulesleutel valideren. We creëren een basisfunctie binnen onze klasse die de sleutel van het formulier valideert. Deze functie moet ook openbaar zijn omdat we deze buiten onze klas gaan gebruiken. De functie valideert de POST-waarde van de formulesleutel ten opzichte van de opgeslagen waarde van de formulesleutel. Voeg deze functie toe aan de PHP-klasse:
// Functie die de formuliersleutel valideerde POST-gegevens public function validate () // We gebruiken de oude formKey en niet de nieuwe gegenereerde versie if ($ _ POST ['form_key'] == $ this-> old_formKey) // The sleutel is geldig, retourneer waar. geef waar terug; else // De sleutel is ongeldig, retourneer false. return false;
Binnen index.php, we valideren de formulesleutel door de functie te gebruiken die we zojuist in onze klas hebben gemaakt. Uiteraard valideren wij alleen na een POST-aanvraag. Voeg de volgende code toe na $ formKey = new formKey ();
$ error = 'Geen fout'; // Is een verzoek? if ($ _ SERVER ['REQUEST_METHOD'] == 'post') // Valideer de formulesleutel als (! isset ($ _ POST ['form_key']) ||! $ formKey-> validate ()) // Formulier sleutel is ongeldig, toon een fout $ error = 'Formulier sleutelfout!'; else // Doe de rest van uw validatie hier $ error = 'Geen form key error!';
We hebben een variabele gemaakt $ error die onze foutmelding opslaat. Als een POST-aanvraag is verzonden, valideren we onze formkey met $ FormKey-> valideren (). Als dit false oplevert, is de form-key ongeldig en wordt er een fout weergegeven. Merk op dat we alleen de sleutel van het formulier valideren - er wordt van je verwacht dat je de rest van het formulier zelf valideert.
In uw HTML kunt u de volgende code plaatsen om het foutbericht weer te geven:
Dit zal het echoën $ error variabele als deze is ingesteld.
Als u uw server start en gaat naar index.php, u ziet ons formulier en het bericht 'Geen fout'. Wanneer u een formulier verzendt, ziet u het bericht 'Geen formuliersleutelfout' omdat dit een geldig POST-verzoek is. Probeer nu de pagina opnieuw te laden en te accepteren wanneer uw browser verzoekt om de POST-gegevens opnieuw te verzenden. U zult zien dat ons script een foutmelding geeft: 'Formulier-sleutelfout!' Uw formulier is nu beveiligd tegen invoer van andere websites en fouten bij het opnieuw laden van pagina's! De fout wordt ook weergegeven na een vernieuwing omdat er een nieuwe form-sleutel is gegenereerd nadat we het formulier hebben verzonden. Dit is goed omdat de gebruiker nu niet per ongeluk een formulier twee keer kan plaatsen.
Hier is de hele PHP- en HTML-code:
index.php
validate ()) // Formulier sleutel is ongeldig, toon een fout $ error = 'Formulier sleutel fout!'; else // Doe de rest van uw validatie hier $ error = 'Geen form key error!'; ?>Formulieren beveiligen met behulp van formulesleutels fomrkey.class.php
old_formKey = $ _SESSION ['form_key']; // Functie voor het genereren van de sleutel van het formulier persoonlijke functie generateKey () // Download het IP-adres van de gebruiker $ ip = $ _SERVER ['REMOTE_ADDR']; // We gebruiken mt_rand () in plaats van rand () omdat het beter is voor het genereren van willekeurige getallen. // We gebruiken 'true' om een langere reeks te krijgen. // Zie http://www.php.net/mt_rand voor een nauwkeurige beschrijving van de functie en meer voorbeelden. $ uniqid = uniqid (mt_rand (), true); // Retourneer de hash-return md5 ($ ip. $ Uniqid); // Functie voor het uitvoeren van de form key public function outputKey () // Genereer de sleutel en sla deze op in de klasse $ this-> formKey = $ this-> generateKey (); // Sla de sleutel op in de sessie $ _SESSION ['form_key'] = $ this-> formKey; // Geef de echo van de formuliersleutel af "formKey. "" /> "; // Functie die de formuliersleutel valideerde POST-gegevens public function validate () // We gebruiken de oude formKey en niet de nieuwe gegenereerde versie if ($ _ POST ['form_key'] == $ this-> old_formKey) // De sleutel is geldig, return true. return true; else // De sleutel is ongeldig, return false. return false;?>Conclusie
Als u deze code toevoegt aan elk belangrijk formulier op uw website, neemt de beveiliging van uw formulier aanzienlijk toe. Het stopt zelfs verfrissende problemen, zoals we in stap 4 zagen. Omdat de formuliersleutel slechts geldig is voor één verzoek, is een dubbele post niet mogelijk.
Dit was mijn eerste zelfstudie, ik hoop dat je het leuk vindt en gebruik het om je veiligheid te verbeteren! Laat het me weten via de reacties. Heb je een betere methode? Laat het ons weten.
Verder lezen
- WordPress maakt ook gebruik van formulesleutels (noce nonces): Wordpress Nonces
- Zeven manieren om veilige PHP-applicaties te schrijven
- Volg ons op Twitter, of abonneer je op de NETTUTS RSS-feed voor meer dagelijkse webontwikkelingen, tuts en artikelen.