Säkra dina formulär med formtangenter

Säkerhet är ett hett ämne. Att säkerställa att dina webbplatser är säkra är oerhört viktigt för alla webbapplikationer. Faktum är att jag tillbringar 70% av min tid som säkrar mina applikationer. En av de viktigaste sakerna vi måste säkra är former. Idag ska vi granska en metod för att förhindra XSS (Cross-site scripting) och Cross-site-förfrågan förfalskning på formulär.

Varför?

POST-data kan skickas från en webbplats till en annan. Varför är det dåligt? Ett enkelt scenario ...

En användare som är inloggad på din webbplats besöker en annan webbplats under sin session. Den här webbplatsen kommer att kunna skicka POST-data till din webbplats - till exempel med AJAX. Eftersom användaren är inloggad på din webbplats kommer den andra webbplatsen även att kunna skicka postdata till säkrade formulär som endast är tillgängliga efter en inloggning..

Vi måste också skydda våra sidor mot attacker med hjälp av cURL

Hur fixar vi det här?

Med formtangenter! Vi lägger till en särskild hash (en formulärnyckel) i alla former för att se till att uppgifterna endast behandlas när den har skickats från din webbplats. Efter ett formulär skickar vårt PHP-skript validering av den inlämnade formulärnyckeln mot formulärnyckeln vi har satt i en session.

Vad vi måste göra:

  1. Lägg till en formulär nyckel till varje form.
  2. Spara formulärnyckeln i en session.
  3. Validera formulärnyckeln efter att en blankett skickats.

Steg 1: En enkel form

Först behöver vi en enkel form för demonstrationsändamål. En av de viktigaste formerna vi måste säkra är inloggningsformuläret. Inloggningsformuläret är sårbara brutna kraftattacker. Skapa en ny fil och spara den som index.php i din webb-rot. Lägg till följande kod i kroppen:

     Säkra formulär med formtangenter   

Nu har vi en enkel XHTML-sida med ett inloggningsformulär. Om du vill använda formulärnycklar på din webbplats kan du ersätta skriptet ovan med din egen inloggningssida. Nu, låt oss fortsätta med den verkliga åtgärden.

Steg 2: Skapa en klass

Vi ska skapa en PHP-klass för våra formulärnycklar. Eftersom varje sida bara kan innehålla ett form nyckel, kunde vi göra en singleton i vår klass för att se till att vår klass används korrekt. Eftersom att skapa singletoner är ett mer avancerat OOP-ämne, kommer vi att hoppa över den delen. Skapa en ny fil som heter formkey.class.php och placera den i din webb-rot. Nu måste vi tänka på de funktioner vi behöver. Först behöver vi en funktion för att skapa en formulärnyckel så att vi kan placera den i vår form. I din PHP-fil placera följande kod:

 

Ovan ser du en klass med tre delar: två variabler och en funktion. Vi gör funktionen privat eftersom den här funktionen endast används av vår produktionfunktioner som vi kommer att skapa senare. I de två variablerna lagrar vi formtangenterna. Dessa är också privata eftersom de endast får användas av funktioner i vår klass.

Nu måste vi tänka på ett sätt att skapa vår formulärnyckel. Eftersom vår formulärnyckel måste vara unik (annars har vi ingen säkerhet) använder vi en kombination av användarnas IP-adress för att binda nyckeln till en användare, mt_rand () för att göra den unik och funktionen uniqid () för att göra det ännu mer unikt. Vi krypterar också denna information med md5 () för att skapa en unik hash som vi sedan kan infoga i våra sidor. Eftersom vi använde md5 () kan en användare inte se vad vi brukade generera nyckeln. Hela funktionen:

 // Funktion för att generera formulärnyckeln privat funktion genereraKey () // Hämta användarens IP-adress $ ip = $ _SERVER ['REMOTE_ADDR']; // Vi använder mt_rand () istället för rand () eftersom det är bättre att generera slumptal. // Vi använder "sant" för att få en längre sträng. // Se http://www.php.net/mt_rand för en exakt beskrivning av funktionen och fler exempel. $ uniqid = uniqid (mt_rand (), true); // Returnera hash return md5 ($ ip. $ Uniqid); 

Sätt in koden ovan i din formkey.class.php fil. Byt ut funktionen med den nya funktionen.

Steg 3: Infoga en formulär nyckel i vår blankett

För detta steg skapar vi en ny funktion som matar ut ett dolt HTML-fält med vår formulärnyckel. Funktionen består av tre steg:

  1. Skapa en formulärnyckel med vår genereraKey () -funktion.
  2. Spara formulärnyckeln i vår $ formKey-variabel och i en session.
  3. Skriv ut HTML-fältet.

Vi heter vår funktion outputKey () och gör det offentligt, eftersom vi måste använda det utanför vår klass. Vår funktion kallar den privata funktionen generateKey () att skapa en ny formulärnyckel och spara den lokalt i en session. Slutligen skapar vi XHTML-koden. Lägg nu till följande kod i vår PHP-klass:

 // Funktion för att mata ut formulärnyckeln public function outputKey () // Generera nyckeln och lagra den inuti klassen $ this-> formKey = $ this-> generateKey (); // Spara formulärnyckeln i sessionen $ _SESSION ['form_key'] = $ this-> formKey; // Utmata formulärnyckeln "formKey. "" /> ";

Nu ska vi lägga till formulärnyckeln till vårt inloggningsformulär för att säkra det. Vi måste inkludera klassen i vår index.php fil. Vi måste också starta sessionen eftersom vår klass använder sessioner för att lagra den genererade nyckeln. För detta lägger vi till följande kod ovanför doktypen och huvudetiketten:

 

Koden ovan är ganska självklarande. Vi startar sessionen (eftersom vi lagrar formulärnyckeln) och laddar PHP-klassfilen. Därefter börjar vi klassen med ny formKey (), Detta kommer att skapa vår klass och lagra den i $ formkey. Nu behöver vi bara redigera vår form så att den innehåller formulärnyckeln:

 
outputKey (); ?>
inmatningstyp = "lösenord" name = "password" />

Och det är allt! Eftersom vi skapade funktionen outputKey (), Vi måste bara inkludera det i vår form. Vi kan använda formulärnycklar i alla former genom att bara lägga till outputKey (); ?> Nu bara granska källan till din webbsida och du kan se att det finns en formulär nyckel kopplad till formuläret. Det enda kvarvarande steget är att validera förfrågningar.

Steg 4: Validerande

Vi kommer inte att validera hela formuläret; bara formulärnyckeln. Valideringen av formuläret är grundläggande PHP och handledning kan hittas över hela webben. Låt oss validera formulärnyckeln. Eftersom vår "generateKey" -funktion överstiger sessionsvärdet lägger vi till en konstruktör i vår PHP-klass. En konstruktör kommer att ringas när vår klass skapas (eller konstrueras). Konstruktören lagrar den föregående nyckeln i klassen innan vi skapar en ny; så vi har alltid den tidigare formulärnyckeln för att validera vår blankett. Om vi ​​inte gjorde det skulle vi inte kunna validera formulärnyckeln. Lägg till följande PHP-funktion i din klass:

 // Konstruktören lagrar formulärnyckeln (om en finns) i vår klassvariabel. funktionen __construct () // Vi behöver den tidigare nyckeln så vi lagrar den om (isset ($ _ SESSION ['form_key'])) $ this-> old_formKey = $ _SESSION ['form_key']; 

En konstruktör ska alltid namnges __konstruera(). När konstruktören kallas kontrollerar vi om en session är inställd, och i så fall lagrar vi den lokalt i vår old_formKey variabel.

Nu kan vi validera vår formulärnyckel. Vi skapar en grundläggande funktion i vår klass som validerar formulärnyckeln. Denna funktion bör också vara offentlig eftersom vi ska använda den utanför vår klass. Funktionen kommer att validera POST-värdet på formulärnyckeln mot det lagrade värdet på formulärnyckeln. Lägg till den här funktionen i PHP-klassen:

 // Funktion som validerad formulärnyckeln POST data public function validate () // Vi använder den gamla formKey och inte den nya genererade versionen om ($ _ POST ['form_key'] == $ this-> old_formKey) // The nyckeln är giltig, returnera sant. återvänd sant;  else // Nyckeln är ogiltig, returnera false. returnera false; 

Inom index.php, vi validerar formulärnyckeln genom att använda den funktion som vi just skapat i vår klass. Naturligtvis validerar vi bara efter en POST-förfrågan. Lägg till följande kod efter $ formKey = ny formKey ();

 $ error = 'Inget fel'; // Är begäran? om $ _ SERVER ['REQUEST_METHOD'] == 'post') // Bekräfta formulärnyckeln om (! isset ($ _ POST ['form_key']) ||! $ formKey-> validate ()) // Form nyckeln är ogiltig, visa ett fel $ error = 'Formulär nyckelfel!';  else // Gör resten av din validering här $ error = 'Inga formulär nyckelfel!'; 

Vi skapade en variabel $ error som lagrar vårt felmeddelande. Om en POST-förfrågan har skickats validerar vi vår formulär med $ FormKey-> bekräfta (). Om detta returneras felaktigt är formulärnyckeln ogiltig och vi visar ett fel. Observera att vi bara validerar formulärnyckeln - du förväntas att validera resten av formuläret själv.

I din HTML kan du placera följande kod för att visa felmeddelandet:

 

Detta kommer att eko på $ error variabel om den är inställd.

Om du startar servern och går till index.php, Du kommer att se vår blankett och meddelandet "No error". När du skickar in ett formulär ser du meddelandet "Något formulär nyckelfel" eftersom det är en giltig POST-förfrågan. Försök nu ladda om sidan och acceptera när din webbläsare begär att POST-data skickas igen. Du ser att vårt skript utlöser ett felmeddelande: 'Form keyfel!' Din formulär skyddas nu mot inmatning från andra webbplatser och fel med sidladdning! Felet visas också efter en uppdatering eftersom en ny formulärnyckel skapades efter att vi skickat in formuläret. Det här är bra för att användaren nu inte kan lägga in en blankett två gånger av misstag.

Fullständig kod

Här är hela PHP och HTML-koden:

index.php

 validera ()) // Formulärnyckeln är ogiltig, visa ett fel $ error = 'Formulär nyckelfel!';  else // Gör resten av din validering här $ error = 'Inga formulär nyckelfel!'; ?>     Säkra formulär med formtangenter   
outputKey (); ?>

fomrkey.class.php

 old_formKey = $ _SESSION ['form_key'];  // Funktion för att generera formulärnyckeln privat funktion genereraKey () // Hämta användarens IP-adress $ ip = $ _SERVER ['REMOTE_ADDR']; // Vi använder mt_rand () istället för rand () eftersom det är bättre att generera slumptal. // Vi använder "sant" för att få en längre sträng. // Se http://www.php.net/mt_rand för en exakt beskrivning av funktionen och fler exempel. $ uniqid = uniqid (mt_rand (), true); // Returnera hash return md5 ($ ip. $ Uniqid);  // Funktion för att mata ut formulärnyckeln public function outputKey () // Generera nyckeln och lagra den inuti klassen $ this-> formKey = $ this-> generateKey (); // Spara formulärnyckeln i sessionen $ _SESSION ['form_key'] = $ this-> formKey; // Utmata formulärnyckeln "formKey. "" /> "; // Funktion som validerat formulärnyckeln POST data public function validate () // Vi använder den gamla formKey och inte den nya genererade versionen om ($ _ POST ['form_key'] == $ this-> old_formKey) // Nyckeln är giltig, returnera true. return true; else // Nyckeln är ogiltig, returnera falskt. return false;?>>

Slutsats

Lägga till den här koden i alla viktiga formulär på din webbplats ökar din formulats säkerhet dramatiskt. Det stannar till och med uppfriskande problem, som vi såg i steg 4. Eftersom formulärnyckeln bara är giltig för en förfrågan, är det inte möjligt att göra ett dubbelt inlägg..

Detta var min första handledning, jag hoppas att du gillar det och använder den för att förbättra din säkerhet! Vänligen meddela dina tankar, via kommentarerna. Har du en bättre metod? Låt oss veta.

Vidare läsning

  • WordPress använder även formulärnycklar (namnger det Nonces): Wordpress Nonces
  • Sju vanor för att skriva säkra PHP-applikationer
  • Följ oss på Twitter, eller prenumerera på NETTUTS RSS-flödet för fler dagliga webbutvecklingstoppar och artiklar.