Bescherm een ​​CodeIgniter-applicatie tegen CSRF

In de tutorial van vandaag zullen we leren hoe je je CodeIgniter (pre 2.0) -toepassing pijnloos kunt beveiligen tegen Cross-Site Request Forgery-aanvallen. De bibliotheek die we vandaag zullen creëren, automatiseert alle beveiligingsmechanismen, waardoor uw site sterker en veiliger wordt.


Stap 1 - De aanvalsvector begrijpen

Cross-Site Request Forgery-aanvallen zijn gebaseerd op onbeveiligde formulieren op uw sites.

Een aanvaller kan een valse vorm op zijn site maken, bijvoorbeeld een zoekformulier. Dit formulier kan verborgen ingangen bevatten die schadelijke gegevens bevatten. Nu wordt het formulier niet daadwerkelijk naar de site van de aanvaller gestuurd om de zoekopdracht uit te voeren; in werkelijkheid wijst de vorm naar jouw site! Omdat uw website erop vertrouwt dat het formulier echt is, doorloopt het de gevraagde (en misschien kwaadwillende) acties en voert het uit.

Stel je voor dat een gebruiker is ingelogd op je site en om een ​​of andere reden wordt omgeleid naar de site van de aanvaller (phishing, XSS, noem maar op). Het formulier van de aanvaller kan naar uw accountverwijderingsformulier op uw site verwijzen. Als de gebruiker een "zoekopdracht" uitvoert op de site van de aanvaller, wordt zijn account vervolgens verwijderd zonder dat hij het weet!

Er zijn talloze manieren om dit soort aanvallen te voorkomen.

  • Controleer de header van de HTTP-verwijzer en kijk of deze bij uw site hoort. Het probleem met deze methode is dat niet alle browsers deze header indienen (ik had dit probleem een ​​keer met IE7); het zou hoe dan ook kunnen worden gesmeed.
  • Een andere methode (degene die we zullen gebruiken), is om een ​​willekeurige reeks (een "token") op elk formulier op te nemen en dat token op te slaan in de sessie van de gebruiker. Op elke POST verzoek, vergelijk het aangeleverde token met datgene dat in de winkel ligt en, als ze verschillen, het verzoek afwijzen. Uw site moet echter nog steeds worden beschermd tegen XSS, want als dit niet het geval is, wordt deze methode onbruikbaar.

Stap 2 - Planning

We moeten drie dingen doen voor elke aanvraag:

  • Als het verzoek een is POST aanvraag, valideer dat het ingediende token.
  • Genereer een token in het geval dat er geen is.
  • Injecteer het token in alle vormen. Dit maakt de methode naadloos en pijnloos, omdat er geen wijziging nodig is in uw weergaven.

Om dit automatisch te doen, zullen we CodeIgniter-haken gebruiken. Met haakjes kunnen we alle acties op verschillende delen van het verzoek uitvoeren. We hebben er drie nodig:

  • post_controller_constructor - Om het ingediende token te controleren, hebben we een post_controller_constructor haak. Het inhaken van deze actie vóór deze gebeurtenis geeft ons niet correct toegang tot het exemplaar van CodeIgniter.
  • Genereer de token - Om het token te genereren, gebruiken we dezelfde haak als hiervoor. Dit stelt ons in staat om toegang te hebben tot het geval we het handmatig in onze standpunten moeten afdrukken.
  • display_override - Om het token automatisch in onze views te injecteren, moeten we de token gebruiken display_override haak. Dit is echter een lastige opsomming, omdat het, zoals de naam al aangeeft, de weergave van de weergaven opheft. We moeten de inhoud zelf uitvoeren als we deze haak gebruiken (raadpleeg de CodeIgniter-documentatie voor meer informatie).

Stap 3 - Token Generation

Laten we beginnen. We gaan stap voor stap om alles zo grondig mogelijk uit te leggen. We maken de methode die het token als eerste genereert, zodat we alles achteraf correct kunnen testen. Maak een bestand in uw system / application / hooks map genaamd "csrf.php"en plak de volgende code:

CI = & get_instance (); 

Hopelijk is wat we hierboven hebben toegevoegd nogal basic voor u. We maken een klasse, genaamd CSRF_Protection, een instantievariabele om de CodeIgniter-instantie te houden, twee statische variabelen die de naam bevatten van de parameter die het token opslaat, en één om het token zelf op te slaan voor eenvoudige toegang door de hele klas. Binnen de klassenbouwer (__construct), halen we eenvoudig de CodeIgniter-instantie op en slaan deze op in onze overeenkomstige instantievariabele.

Notitie: De "naam van de parameter" is de naam van het veld dat het token bevat. Dus op de formulieren is dit de naam van de verborgen invoer.

Na de klassenconstructor plakt u de volgende code:

/ ** * Genereert een CSRF-token en slaat het op tijdens de sessie. Er wordt slechts één token per sessie gegenereerd. * Dit moet worden gekoppeld aan een haak na de controller en vóór de hook * die de methode inject_tokens () aanroept. * * @return void * @author Ian Murray * / public function generate_token () // Laad sessiebibliotheek indien niet geladen $ this-> CI-> laad-> bibliotheek ('sessie'); if ($ this-> CI-> session-> userdata (self :: $ token_name) === FALSE) // Genereer een token en sla deze op tijdens de sessie, omdat de oude lijkt te zijn verlopen. self :: $ token = md5 (uniqid (). microtime (). rand ()); $ this-> CI-> session-> set_userdata (self :: $ token_name, self :: $ token);  else // Stel het in op lokale variabele voor eenvoudige toegang zelf :: $ token = $ this-> CI-> session-> userdata (self :: $ token_name); 

Stap voor stap:

  • We laden de sessiebibliotheek voor het geval deze niet automatisch wordt geladen. We hebben dit nodig om het token op te slaan.
  • We bepalen of het token al is gegenereerd. Als het gebruikersgegevens methode retourneert VALSE, dan is het token nog niet aanwezig.
  • We genereren een token en slaan dit op in de klassenvariabele voor eenvoudige toegang. Het token kan bijna alles zijn. Ik heb die drie functies gebruikt om ervoor te zorgen dat het zo is heel willekeurig.
  • We slaan het vervolgens op in de sessievariabele met de eerder geconfigureerde naam.
  • Als het token al aanwezig was, wij niet doen genereer het en sla het in plaats daarvan op in de klassenvariabele voor eenvoudige toegang.

Stap 4 - Token-validatie

We moeten ervoor zorgen dat het token is ingediend en is geldig in het geval dat het verzoek een is POST verzoek. Ga je gang en plak de volgende code in je csrf.php het dossier:

/ ** * Valideert een ingediend token wanneer een POST-aanvraag is gedaan. * * @return void * @author Ian Murray * / public function validate_tokens () // Is dit een postverzoek? if ($ _SERVER ['REQUEST_METHOD'] == 'POST') // Is het tokenveld ingesteld en geldig? $ placed_token = $ this-> CI-> input-> post (self :: $ token_name); if ($ posted_token === FALSE || $ posted_token! = $ this-> CI-> session-> userdata (self :: $ token_name)) // Ongeldig verzoek, verzendfout 400. show_error ('Verzoek was ongeldig. Tokens kwamen niet overeen. ', 400); 
  • Hierboven valideren we het verzoek, maar enkel en alleen als het een is POST aanvraag, wat betekent dat er in feite een formulier is ingediend. We controleren dit door een kijkje te nemen naar de REQUEST_METHOD binnen de $ _SERVER super globaal.
  • Controleer of het token daadwerkelijk is gepost. Als de uitvoer van $ This-> CI-> input-> post (zelf :: $ TOKEN_NAME) is VALSE, dan is het token nooit gepost.
  • Als het token niet is gepost of als het niet gelijk is aan het gegenereerde token, weiger het verzoek dan met een "Bad Request" -fout.

Stap 5 - Injecteer Tokens in de views

Dit is het leuke gedeelte! We moeten de tokens in alle vormen injecteren. Om ons leven gemakkelijker te maken, gaan we twee metatags in onze plaatsen (Rails-achtige). Op die manier kunnen we het token ook opnemen in AJAX-verzoeken.

Voeg de volgende code toe aan uw csrf.php het dossier:

/ ** * Dit injecteert verborgen tags op alle POST-formulieren met het csrf-token. * Injecteert ook meta-headers in  van uitvoer (indien aanwezig) voor eenvoudige toegang * vanuit JS-frameworks. * * @return void * @author Ian Murray * / public function inject_tokens () $ output = $ this-> CI-> output-> get_output (); // Injecteer in formulier $ output = preg_replace ('/ (<(form|FORM)[^>] * (methode | METHOD) = "(bericht | POST)" [^>] *>) / ',' $ 0', $ uitvoer); // Injecteer in  $ output = preg_replace ('/ (<\/head>) / ',''. "\ n". ''. "\ n". '$ 0', $ uitvoer); $ This-> CI-> uitgang -> _ display ($ output); 
  • Aangezien dit een is display_override haak, we moeten de gegenereerde uitvoer ophalen uit CodeIgniter. We doen dit door de $ This-> CI-> output-> get_output () methode.
  • Om de tags in onze formulieren te injecteren, gebruiken we reguliere expressies. De expressie zorgt ervoor dat we een verborgen invoertag (die ons gegenereerde token bevat) alleen in vormen met een methode van het type injecteert POST.
  • We moeten ook onze metatags injecteren in de hoofd (indien aanwezig). Dit is eenvoudig, omdat de tag voor de afsluitende kop slechts eenmaal per bestand aanwezig mag zijn.
  • Ten slotte, omdat we de display_override hook, de standaardmethode om uw weergave weer te geven, wordt niet aangeroepen. Deze methode omvat allerlei dingen, die we niet zouden moeten doen - alleen voor het doel van het injecteren van wat code. Als we het zelf noemen, lost dit dit op.

Stap 6 - Haken

Last but not least, we moeten zelf de hooks maken - dus onze methoden worden gebeld. Plak de volgende code in uw system / application / config / hooks.php het dossier:

// // CSRF Beschermingshaken, raak deze alleen aan als u weet wat u // doet. // // DE ORDE VAN DEZE HAKEN IS EXTREEM BELANGRIJK !! // // DIT MOET EERST IN DE HAAKLIJST post_controller_constructor PLAATSEN. $ hook ['post_controller_constructor'] [] = array (// Let op de "[]", dit is niet de enige post_controller_constructor hook 'class' => 'CSRF_Protection', 'function' => 'validate_tokens', 'filename' = > 'csrf.php', 'filepath' => 'hooks'); // Genereert het token (MOET GEBEUREN NA DE VALIDERING IS GEMAAKT, MAAR VOORDAT DE CONTROLLER // WORDT UITGEVOERD, ANDERS HEEFT GEBRUIKER GEEN TOEGANG TOT EEN GELDIG TOKEN VOOR AANGEPASTE VORMEN). $ hook ['post_controller_constructor'] [] = array (// Let op de "[]", dit is niet de enige post_controller_constructor hook 'class' => 'CSRF_Protection', 'function' => 'generate_token', 'filename' = > 'csrf.php', 'filepath' => 'hooks'); // Dit injecteert tokens op alle vormen $ hook ['display_override'] = array ('class' => 'CSRF_Protection', 'function' => 'inject_tokens', 'filename' => 'csrf.php', 'filepath' => 'hooks');
  • De eerste haak roept de validate_tokens methode. Dit is niet de enige post_controller_constructor haak, dus we moeten die haakjes toevoegen ("[]"). Raadpleeg de documentatie over CodeIgniter-hooks voor meer informatie.
  • De tweede haak, die ook een post_controller_constructor, genereert het token in het geval het nog niet is gegenereerd.
  • De derde is het uitschakelen van het display. Deze haak zal onze tokens injecteren in de het formulier en de hoofd.

Afsluiten

Met minimale inspanning hebben we een aardige bibliotheek voor onszelf gebouwd.

U kunt deze bibliotheek gebruiken in elk project en het beschermt uw site automatisch tegen CSRF.

Als je wilt bijdragen aan dit kleine project, kun je hieronder een reactie achterlaten, of het project aanvullen op GitHub. Als alternatief, vanaf CodeIgniter v2.0, is nu bescherming tegen CSRF-aanvallen ingebouwd in het raamwerk!