Beskytt en CodeIgniter-applikasjon mot CSRF

I dagens veiledning vil vi lære å smertefritt beskytte CodeIgniter (pre 2.0) -applikasjonen mot overgrep på forespørsler om overfallsforespørsler. Biblioteket vi skal skape i dag, vil automatisere alle beskyttelsesmekanismer, noe som gjør nettstedet ditt sterkere og sikrere.


Trinn 1 - Forstå angrepvektoren

Forsøk på overfallsforespørsler er basert på ubeskyttede skjemaer på nettstedene dine.

En angriper kan opprette en falsk form på nettstedet hans - for eksempel et søkeskjema. Dette skjemaet kan ha skjulte innganger som inneholder skadelige data. Nå sendes skjemaet ikke til angriperens nettsted for å utføre søket; I virkeligheten peker skjemaet på din nettstedet! Siden nettstedet ditt vil stole på at skjemaet er ekte, går det gjennom og utfører de forespurte (og kanskje skadelige) handlingene.

Tenk deg at en bruker er logget inn på nettstedet ditt, og omdirigeres til angriperens nettsted av en eller annen grunn (phishing, XSS, navnet ditt). Angriperens skjema kan peke på skjemaet for sletting av konto på nettstedet ditt. Hvis brukeren utfører et "søk" på angriperne, blir hans konto slettet uten at han vet!

Det er mange måter å forhindre denne typen angrep på.

  • Sjekk HTTP Referer header og se om den tilhører nettstedet ditt. Problemet med denne metoden er at ikke alle nettlesere sender denne overskriften (jeg personlig hadde dette problemet en gang med IE7); det kan til slutt bli smidd.
  • En annen metode (den vi skal bruke), er å inkludere en tilfeldig streng (et "token") på hver form, og lagre det token på brukerens økt. På hver POST forespørsel, sammenlign den innsendte token til den i butikken, og, hvis de er forskjellige, nekter forespørselen. Nettstedet ditt må fortsatt være beskyttet mot XSS, men fordi det ikke er, blir denne metoden ubrukelig.

Trinn 2 - Planlegging

Vi må gjøre tre ting for hver forespørsel:

  • Hvis forespørselen er en POST forespørsel, validere den innsendte token.
  • Generer et token i tilfelle det ikke er en.
  • Injiser token i alle former. Dette gjør metoden sømløs og smertefri, siden ingen endring er nødvendig på dine synspunkter.

For å gjøre dette automatisk bruker vi CodeIgniter kroker. Kroker gir oss mulighet til å utføre alle handlinger på ulike deler av forespørselen. Vi trenger tre:

  • post_controller_constructor - For å sjekke det innsendte token, trenger vi en post_controller_constructor kroken. Hooking denne handlingen før denne hendelsen gir oss ikke tilgang til CodeIgniter's forekomst på riktig måte.
  • Generer Token - For å generere token bruker vi samme krok som tidligere. Dette tillater oss å få tilgang til det dersom vi måtte skrive ut det manuelt i våre synspunkter.
  • display_override - For å injisere token automatisk i våre visninger, må vi bruke display_override kroken. Dette er en vanskelig krog skjønt, siden, som navnet tilsier, er det forstyrret visning av visningene. Vi må selv utgive innholdet hvis vi bruker denne kroken (se CodeIgniter dokumentasjonen for mer info).

Trinn 3 - Token Generation

La oss komme i gang. Vi skal gå trinnvis for å forklare alt så grundig som mulig. Vi lager den metoden som genererer symbolet først, slik at vi kan teste alt riktig etterpå. Lag en fil i din system / applikasjon / kroker mappe kalt "csrf.php", og lim inn følgende kode:

CI = & get_instance (); 

Forhåpentligvis, hva vi har lagt til ovenfor, bør se litt grunnleggende til deg. Vi lager en klasse, kalt CSRF_Protection, en instansvariabel for å holde CodeIgniter-forekomsten, to statiske variabler for å holde navnet på parameteren som vil lagre token, og en for å lagre token selv for enkel tilgang i hele klassen. Innenfor klassekonstruktøren (__construct), henter vi bare CodeIgniter-forekomsten, og lagrer den i vår tilsvarende instansvariabel.

Merk: "Navn på parameteren" er navnet på feltet som holder token. Så på skjemaene er det navnet på den skjulte inngangen.

Etter klassekonstruktøren, lim inn følgende kode:

/ ** * Genererer et CSRF-token og lagrer det på økt. Kun én token per sesjon er generert. * Dette må være bundet til en etterkontrollkrok, og før kroken * som kaller injeksjonsmetoden (). * * @return void * @author Ian Murray * / offentlig funksjon generate_token () // Legg inn sesjonsbibliotek hvis ikke lastet $ this-> CI-> load-> library ('session'); hvis ($ this-> CI-> session-> userdata (self :: $ token_name) === FALSE) // Generer et token og lagre det på økt, siden den gamle ser ut til å være utløpt. selv: $ token = md5 (uniqid (). mikrotime () .rand ()); $ this-> CI-> session-> set_userdata (selv :: $ token_name, self :: $ token);  annet // Sett det til lokal variabel for enkel tilgang selv :: $ token = $ this-> CI-> session-> userdata (selv :: $ token_name); 

Steg for steg:

  • Vi laster inn sesjonsbiblioteket, hvis det ikke lastes automatisk. Vi trenger dette for å lagre token.
  • Vi avgjør om token allerede var generert. Hvis brukerdata metode returnerer FALSK, så er token ennå ikke til stede.
  • Vi genererer et token og lagrer det i klassevariabelen for enkel tilgang. Token kan være nesten alt egentlig. Jeg brukte disse tre funksjonene for å sikre at det er veldig tilfeldig.
  • Vi lagrer det i sesjonsvariabelen med navnet konfigurert tidligere.
  • Hvis token allerede var tilstede, vi ikke generer den og lagre den i klassevariabelen for å få enkel tilgang.

Trinn 4 - Token validering

Vi må sørge for at token ble sendt inn, og er gyldig dersom forespørselen er en POST be om. Gå videre og lim inn følgende kode i din csrf.php fil:

/ ** * Validerer en sendt token når POST-forespørsel er gjort. * * @return void * @author Ian Murray * / offentlig funksjon validate_tokens () // Er dette en postanmodning? hvis ($ _SERVER ['REQUEST_METHOD'] == 'POST') // Er token-feltet satt og gyldig? $ posted_token = $ this-> CI-> input-> post (selv :: $ token_name); hvis ($ posted_token === FALSE || $ posted_token! = $ this-> CI-> økt-> userdata (selv :: $ token_name)) // Ugyldig forespørsel, send feil 400. show_error ('Forespørsel var ugyldig. Tokene stemte ikke overens. ', 400); 
  • Ovenstående bekrefter vi forespørselen, men bare hvis det er en POST forespørsel, noe som betyr at et skjema faktisk ble sendt inn. Vi sjekker dette ved å ta en titt på REQUEST_METHOD innen $ _SERVER super global.
  • Sjekk om token faktisk ble lagt ut. Hvis utgangen av $ Dette-> KI> input-> innlegget (selv :: $ symbolnavn) er FALSK, så ble token aldri postet.
  • Hvis token ikke ble lagt ut eller hvis den ikke er lik den vi genererte, nekter du forespørselen med en feilmelding.

Trinn 5 - Injiser Token i visningene

Dette er den morsomme delen! Vi må injisere tokens i alle former. For å gjøre livet enklere for oss selv, skal vi plassere to metatagger innenfor vårt (Skinner-lignende). På den måten kan vi også inkludere token i AJAX-forespørsler.

Legg til følgende kode i din csrf.php fil:

/ ** * Dette injiserer skjulte koder på alle POST-skjemaer med csrf-token. * Injektorer også meta headers i  av utdata (hvis eksisterende) for enkel tilgang * fra JS-rammer. * * @return void * @author Ian Murray * / offentlig funksjon inject_tokens () $ output = $ this-> CI-> output-> get_output (); // Injiser i form $ output = preg_replace ('/ (<(form|FORM)[^>] * (metode | METHOD) = "(post | POST)" [^>] *> / ',' $ 0', $ output); // Injiser inn  $ output = preg_replace ('/ (<\/head>) / ',''. "\ n". ''. "\ n". '$ 0', $ output); $ Dette-> KI> output -> _ display ($ utgang); 
  • Siden dette er en display_override krok, vi må hente den genererte produksjonen fra CodeIgniter. Vi gjør dette ved å bruke $ Dette-> KI> avgi> get_output () metode.
  • For å injisere kodene i våre skjemaer, bruker vi vanlige uttrykk. Uttrykket sikrer at vi injiserer en skjult inntagsmerke (som inneholder vårt genererte token) bare i skjemaer med en type metode POST.
  • Vi må også injisere våre metatagger i Overskrift (hvis tilstede). Dette er enkelt, siden lukkehodetiketten bare skal være til stede en gang per fil.
  • Til slutt, siden vi bruker display_override krok, vil standardmetoden for å vise visningen ikke bli kalt. Denne metoden inkluderer alle slags ting, som vi ikke bør - bare for å injisere noen kode. Å ringe det selv løser dette.

Trinn 6 - Kroker

Sist, men ikke minst, må vi lage krokene selv - så våre metoder blir kalt. Lim inn følgende kode i din Systemet / applikasjon / konfig / hooks.php fil:

// // CSRF Beskyttelses kroker, rør ikke disse med mindre du vet hva du gjør. // / BESTEMMELSEN AV DENNE HØYKEN ER EKSTREMT VIKTIG! // // DETTE SKAL GÅ FØRST I postlisten etter_controller_constructor. $ hook ['post_controller_constructor'] [] = array (// Husk "[]", dette er ikke den eneste post_controller_constructor hook' class' => 'CSRF_Protection', 'function' => 'validate_tokens', 'filnavn' = > 'csrf.php', 'filepath' => 'kroker'); // Genererer token (SKAL GJELDE ETTER VALIDASJONEN ER GJELDT, MEN FØR CONTROLLEREN / / UTFØRES, ANDRE BRUKER IKKE HAR TILGJENGELIGHET TIL GJELDENDE TOKEN FOR TOLDFORMER). $ krok ['post_controller_constructor'] [] = array (// Mind "[]", dette er ikke den eneste post_controller_constructor hook' class' => 'CSRF_Protection', 'function' => 'generate_token', 'filnavn' = ' > 'csrf.php', 'filepath' => 'kroker'); // Dette injiserer tokens på alle former $ hook ['display_override'] = array ('class' => 'CSRF_Protection', 'function' => 'inject_tokens', 'filnavn' => 'csrf.php', 'filepath' => 'kroker');
  • Den første kaken kaller validate_tokens metode. Dette er ikke det eneste post_controller_constructor krok, så vi må legge til de parentesene ("[]"). Se dokumentasjonen på CodeIgniter kroker for mer info.
  • Den andre kroken, som også er a post_controller_constructor, genererer token i tilfelle det ikke har blitt generert ennå.
  • Den tredje er skjermen forstyrre. Denne kroken vil injisere våre tokens i skjema og Overskrift.

Wrapping Up

Med minimal innsats har vi bygget et ganske godt bibliotek for oss selv.

Du kan bruke dette biblioteket i et hvilket som helst prosjekt, og det vil automatisk beskytte nettstedet mot CSRF.

Hvis du vil bidra til dette lille prosjektet, vennligst legg igjen en kommentar nedenfor, eller gaffel prosjektet på GitHub. Alternativt, ifølge CodeIgniter v2.0, er beskyttelse mot CSRF-angrep nå innebygd i rammen!