Skydda en CodeIgniter-ansökan mot CSRF

I dagens handledning lär vi oss hur du smärtfritt skyddar din CodeIgniter (pre 2.0) -applikation mot angreppssökningar angående Cross-Site Request Forgery. Biblioteket vi ska skapa idag kommer att automatisera alla skyddsmekanismer, vilket gör din webbplats starkare och säkrare.


Steg 1 - Förstå angreppsvektorn

Ansökningar om förfalskning på flera sidor är baserade på oskyddade formulär på dina webbplatser.

En angripare kan skapa en falsk form på sin webbplats - till exempel en sökformulär. Den här blanketten kan ha gömda inmatningar som innehåller skadliga data. Nu skickas formuläret inte till attackerens webbplats för att utföra sökningen. I verkligheten pekar formuläret på din webbplats! Eftersom din webbplats kommer att lita på att formuläret är äkta, går det igenom och utför de begärda (och kanske skadliga) åtgärderna.

Tänk dig att en användare är inloggad på din webbplats och omdirigeras till attackerens webbplats av någon anledning (phishing, XSS, du heter det). Attackerens formulär kan peka på ditt kontoets borttagningsformulär på din webbplats. Om användaren utför en "sök" på attackerens webbplats kommer hans konto att raderas utan att han vet!

Det finns många sätt att förhindra dessa typer av attacker.

  • Kontrollera HTTP Referer header och se om den tillhör din webbplats. Problemet med den här metoden är att inte alla webbläsare skickar in denna rubrik (jag hade personligen problemet ett gång med IE7); det skulle kunna smidda ändå.
  • En annan metod (den som vi kommer att använda) är att inkludera en slumpmässig sträng (en "token") på varje blankett och lagra den token på användarens session. På varje POSTA begära, jämföra det inlämnade token till den som finns i butiken och, om de skiljer sig, neka förfrågan. Din webbplats måste ändå skyddas mot XSS, för, om det inte är så, blir den här metoden värdelös.

Steg 2 - Planering

Vi måste göra tre saker för varje förfrågan:

  • Om förfrågan är a POSTA begära, validera det inlämnade token.
  • Generera en token om det inte finns någon.
  • Injicera token i alla former. Detta gör metoden sömlös och smärtfri, eftersom ingen ändring behövs på dina åsikter.

För att göra detta automatiskt använder vi CodeIgniter krokar. Krokar tillåter oss att utföra alla åtgärder på olika delar av förfrågan. Vi behöver tre:

  • post_controller_constructor - För att kontrollera den inlämnade token behöver vi en post_controller_constructor krok. Att hämta denna åtgärd före denna händelse ger oss inte tillgång till CodeIgniter-förekomsten korrekt.
  • Generera Token - För att generera token använder vi samma krok som tidigare. Detta gör det möjligt för oss att få tillgång till det om vi behövde skriva ut det manuellt i våra synpunkter.
  • display_override - För att injicera token automatiskt i våra synpunkter måste vi använda display_override krok. Det här är en knepig krok, eftersom det, som namnet antyder, överstyrer visningen av vyerna. Vi behöver själva utmata innehållet om vi använder den här kroken (kolla CodeIgniter-dokumentationen för mer info).

Steg 3 - Token Generation

Låt oss börja. Vi går steg för steg för att förklara allt så noggrant som möjligt. Vi skapar den metod som genererar token först, så vi kan testa allt rätt efteråt. Skapa en fil i din system / application / krokar mapp som heter "csrf.php"och klistra in följande kod:

CI = & get_instance (); 

Förhoppningsvis, vad vi har lagt till ovan borde se ganska grundläggande ut för dig. Vi skapar en klass, kallad CSRF_Protection, en instansvariabel för att hålla CodeIgniter-förekomsten, två statiska variabler för att hålla namnet på den parameter som lagrar token och en för att lagra token själv för enkel åtkomst i hela klassen. Inom klasskonstruktören (__konstruera), hämtar vi bara CodeIgniter-förekomsten och lagrar den i vår motsvarande instansvariabel.

Notera: "Parameterns namn" är namnet på fältet som innehåller token. Så på blanketterna är det namnet på den dolda inmatningen.

Efter klasskonstruktören klistra in följande kod:

/ ** * Genererar en CSRF-token och lagrar den på session. Endast en token per session genereras. * Detta måste vara kopplat till en krock efter styrenheten och före kroken * som kallar injektionsmetoden (). * * @return void * @author Ian Murray * / allmän funktion generate_token () // Ladda upp session bibliotek om inte laddat $ this-> CI-> load-> library ('session'); om ($ this-> CI-> session-> userdata (self :: $ token_name) === FALSE) // Skapa en token och lagra den på sessionen, eftersom gamla verkar ha gått ut. själv: $ token = md5 (uniqid (). mikrotime () .rand ()); $ this-> CI-> session-> set_userdata (själv :: $ token_name, self :: $ token);  else // Ställ in den på lokal variabel för enkel åtkomst själv :: $ token = $ this-> CI-> session-> userdata (self :: $ token_name); 

Steg för steg:

  • Vi laddar sessionsbiblioteket om det inte laddas automatiskt. Vi behöver detta för att lagra token.
  • Vi avgör om token redan genererades. Om användardata metod returnerar FALSK, då är symbolen ännu inte närvarande.
  • Vi genererar en token och lagrar den i klassvariabeln för enkel åtkomst. Token kan vara nästan vad som helst. Jag använde dessa tre funktioner för att se till att det är mycket slumpmässig.
  • Vi lagrar sedan den i sessionvariabeln med namnet som tidigare konfigurerats.
  • Om token redan var närvarande, vi inte generera den och istället spara den i klassvariabeln för enkel åtkomst.

Steg 4 - Token Validation

Vi måste se till att token lämnades in, och gäller om begäran är en POSTA begäran. Fortsätt och klistra in följande kod i din csrf.php fil:

/ ** * Validerar en inloggad token när POST-förfrågan görs. * * @return void * @author Ian Murray * / allmän funktion validate_tokens () // Är det här en postförfrågan? om ($ _SERVER ['REQUEST_METHOD'] == 'POST') // Är tokenfältet inställt och giltigt? $ posted_token = $ this-> CI-> input-> post (själv :: $ token_name); om $ posted_token === FALSE || $ posted_token! = $ this-> CI-> session-> userdata (self :: $ token_name)) // Ogiltig förfrågan, skicka fel 400. show_error ('Förfrågan var ogiltig. Token matchade inte. ', 400); 
  • Ovan validerar vi förfrågan, men endast om det är en POSTA begäran, vilket innebär att en blankett faktiskt lämnades in. Vi kontrollerar detta genom att ta en titt på REQUEST_METHOD inom $ _SERVER super global.
  • Kontrollera om token faktiskt postades. Om utsignalen från $ This-> Cl> input> post (self :: $ TOKEN_NAME) är FALSK, då token var aldrig postad.
  • Om token inte postades eller om den inte är lika med den som vi genererade, neka förfrågan med ett felmeddelande "Bad Request".

Steg 5 - Injicera Tokens i Visningarna

Det här är den roliga delen! Vi måste injicera tokens i alla former. För att göra livet enklare för oss ska vi placera två metataggar inom vår (Räls-liknande). På så sätt kan vi inkludera token i AJAX-förfrågningar också.

Lägg till följande kod i din csrf.php fil:

/ ** * Detta injicerar dolda taggar på alla POST-formulär med csrf-token. * Injektioner också meta headers i  av utdata (om existerar) för enkel åtkomst * från JS-ramar. * * @return void * @author Ian Murray * / allmän funktion inject_tokens () $ output = $ this-> CI-> output-> get_output (); // Injicera i form $ output = preg_replace ('/ (<(form|FORM)[^>] * (metod | METHOD) = "(post | POST)" [^>] *) / ',' $ 0', $ output); // Injicera in i  $ output = preg_replace ('/ (<\/head>) / ',''. "\ n". ''. "\ n". '$ 0', $ output); $ This-> Cl> output -> _ display ($ output); 
  • Eftersom detta är en display_override krok, vi måste hämta den genererade produktionen från CodeIgniter. Vi gör detta genom att använda ($ This-> Cl> output-> get_output) metod.
  • För att injicera taggarna i våra formulär använder vi vanliga uttryck. Uttrycket försäkrar att vi injicerar en dold inmatnings tagg (som innehåller vår genererade token) endast i formulär med en typmetod POSTA.
  • Vi måste också injicera våra metataggar i rubrik (om närvarande). Det här är enkelt, eftersom stängningshuvudet ska vara en gång per fil.
  • Slutligen, eftersom vi använder display_override krok, standardmetoden för att visa din vy kommer inte att ringas. Denna metod inkluderar alla sorters saker, som vi inte borde - bara för att injicera någon kod. Ringa det själva löser detta.

Steg 6 - Krokar

Sist men inte minst måste vi skapa krokarna själva, så våra metoder kallas. Klistra in följande kod i din system / application / config / hooks.php fil:

// // CSRF Skyddskrokar, rör inte dessa om du inte vet vad du gör. // // BESTÄLLNINGEN AV DESS HOPPAR ÄR MYCKET VIKTIGT !! // // DETTA ÄR GÅ FIRST I HOOK LISTEN post_controller_constructor. $ krok ['post_controller_constructor'] [] = array (// Mind "[]", det här är inte den enda post_controller_constructor hook' class' => 'CSRF_Protection', 'function' => 'validate_tokens', 'filnamn' = > 'csrf.php', 'filepath' => 'krokar'); // Genererar token (SKALL HOPPAS EFTER VALIDATIONEN HAR GJUTS, MEN INNAN KONTROLLEREN / UTFÖRS, ANVÄNDAR ANDRA ANVÄNDAR INTE TILLGÅNG TILL GILTIGT TOKEN FÖR TULLFORMER). $ hook ['post_controller_constructor'] [] = array (// Håll "[]", det här är inte den enda post_controller_constructor hook' class' => 'CSRF_Protection', 'function' => 'generate_token', 'filename' = ' > 'csrf.php', 'filepath' => 'krokar'); // Detta sprutar tokens i alla former $ hook ['display_override'] = array ('class' => 'CSRF_Protection', 'function' => 'inject_tokens', 'filnamn' => 'csrf.php', 'filepath' => "krokar");
  • Den första kroken kallar validate_tokens metod. Detta är inte det enda post_controller_constructor krok, så vi måste lägga till dessa fästen ("[]"). Se dokumentationen på CodeIgniter krokar för mer info.
  • Den andra kroken, som också är a post_controller_constructor, genererar token om det inte har genererats än.
  • Den tredje är displayöverstyrningen. Denna krok kommer att injicera våra tokens i form och den rubrik.

Avslutar

Med minimal ansträngning har vi byggt ett ganska bra bibliotek för oss själva.

Du kan använda det här biblioteket i något projekt, och det kommer automatiskt att skydda din webbplats mot CSRF.

Om du vill bidra till det här lilla projektet, vänligen lämna en kommentar nedan, eller gaffel projektet på GitHub. Alternativt är, enligt CodeIgniter v2.0, skydd mot CSRF-attacker nu inbyggt i ramverket!