Protejați o aplicație CodeIgniter împotriva CSRF

În tutorialul de astăzi, vom învăța cum să protejăm fără durere aplicația CodeIgniter (pentru versiunea 2.0) împotriva atacurilor de falsificare a solicitărilor transfrontaliere. Biblioteca pe care o vom crea astăzi va automatiza toate mecanismele de protecție, făcând site-ul dvs. mai puternic și mai sigur.


Pasul 1 - Înțelegerea vectorului de atac

Solicitările de interogare de tip Cross-Site se bazează pe formulare neprotejate pe site-urile dvs..

Un atacator ar putea crea o formă falsă pe site-ul său - de exemplu un formular de căutare. Acest formular ar putea avea ascunse intrări care conțin date rău intenționate. Acum, formularul nu este trimis la site-ul atacatorului pentru a efectua căutarea; în realitate, forma subliniază ta site-ul! Întrucât site-ul dvs. web va avea încredere că formularul este autentic, acesta trece prin și execută acțiunile solicitate (și probabil rău intenționate).

Imaginați-vă că un utilizator este conectat la site-ul dvs. și este redirecționat către site-ul atacatorului din anumite motive (phishing, XSS, îl numiți). Formularul atacatorului poate indica pe site-ul dvs. formularul de ștergere a contului. Dacă utilizatorul efectuează o căutare pe site-ul atacatorilor, contul său va fi apoi șters fără ca el să știe!

Există numeroase modalități de a preveni aceste tipuri de atacuri.

  • Verificați antetul HTTP Referer și vedeți dacă aparține site-ului dvs. Problema cu această metodă este că nu toate browserele trimit acest antet (eu personal am avut această problemă odată cu IE7); ar putea fi forjate oricum.
  • O altă metodă (cea pe care o vom folosi) este de a include un șir aleatoriu (un "token") pe fiecare formular și de a stoca tokenul în sesiunea utilizatorului. Pe fiecare POST solicitați, comparați tokenul trimis cu cel de pe magazin și, dacă diferă, respingeți solicitarea. Totuși, site-ul dvs. trebuie protejat împotriva XSS, deoarece, dacă nu este, această metodă devine inutilă.

Pasul 2 - Planificarea

Va trebui să facem trei lucruri pentru fiecare solicitare:

  • Dacă cererea este a POST solicitați, validați că jetonul trimis.
  • Generați un jeton în cazul în care nu există unul.
  • Injectați tokenul în toate formele. Aceasta face ca metoda să fie fără probleme și fără durere, deoarece nu este necesară modificarea opiniilor dvs..

Pentru a face acest lucru automat, vom folosi cârligele CodeIgniter. Cârligele ne permit să executăm toate acțiunile din diferite părți ale cererii. Avem nevoie de trei:

  • post_controller_constructor - Pentru a verifica tokenul trimis, avem nevoie de a post_controller_constructor cârlig. Apelarea acestei acțiuni înainte de acest eveniment nu ne permite să accesăm corect instanța CodeIgniter.
  • Generați Tokenul - Pentru a genera tokenul, vom folosi același cârlig ca înainte. Acest lucru ne permite să avem acces la acesta în cazul în care trebuie să îl tipărim manual în opiniile noastre.
  • display_override - Pentru a injecta automat jetonul în vederile noastre, va trebui să folosim display_override cârlig. Acesta este un cârlig dificil deși, deoarece, după cum sugerează și numele său, acesta suprapune afișarea vederilor. Trebuie să scoatem conținutul pe cont propriu dacă folosim acest cârlig (verificați documentația CodeIgniter pentru mai multe informații).

Pasul 3 - Generarea de jetoane

Să începem. Vom merge pas cu pas pentru a explica totul cât mai bine posibil. Vom crea metoda care generează primul jeton, astfel încât să putem testa totul corect după aceea. Creați un fișier în dvs. sistem / aplicație / cârlige dosarul numit "csrf.php"și introduceți următorul cod:

CI = & get_instance (); 

Sperăm că ceea ce am adăugat mai sus ar trebui să pară mai degrabă pentru tine. Creăm o clasă, numită CSRF_Protection, o variabilă de instanță pentru a ține instanța CodeIgniter, două variabile statice pentru a ține numele parametrului care va stoca tokenul și unul pentru a stoca tokenul în sine pentru a avea acces ușor în întreaga clasă. În cadrul constructorului de clasă (__construi), pur și simplu preluăm instanța CodeIgniter și stocăm-o în variabila de instanță corespunzătoare.

Notă: "Numele parametrului" este numele câmpului care deține jetonul. Deci, pe formulare, este numele intrării ascunse.

După constructorul de clasă, lipiți următorul cod:

/ ** * generează un jeton CSRF și îl stochează în sesiune. Se generează doar un singur simbol pe sesiune. * Aceasta trebuie legată de un cârlig post-controler și înainte de cârligul * care apelează metoda inject_tokens (). * * @return void * @author Ian Murray * / funcția publică generate_token () // Încărcați biblioteca sesiunilor dacă nu încărcați $ this-> CI-> load-> library ('session'); dacă ($ this-> CI-> session-> userdata (self :: $ token_name) === FALSE) // Generați un jeton și păstrați-l în sesiune, deoarece cel vechi pare să fi expirat. auto :: $ token = md5 (uniqid () .microtime () rand ()); $ this-> CI-> sesiune-> set_userdata (auto :: $ token_name, auto :: $ token);  altfel // Setați-l la variabila locală pentru accesul facil la sine :: $ token = $ this-> CI-> session-> userdata (self :: $ token_name); 

Pas cu pas:

  • Încărcăm biblioteca de sesiuni, în cazul în care nu se încarcă automat. Avem nevoie de acest lucru pentru a stoca tokenul.
  • Determinăm dacă jetonul a fost deja generat. În cazul în care datele utilizatorului revine metoda FALS, atunci tokenul nu este încă prezent.
  • Generăm un jeton și îl stocăm în variabila de clasă pentru a avea acces ușor. Simbolul ar putea fi aproape orice. Am folosit aceste trei funcții pentru a vă asigura că este foarte întâmplător.
  • Apoi îl stocăm în variabila sesiune cu numele configurat anterior.
  • Dacă simbolul era deja prezent, noi nu face generați-o și stocați-o în clasa variabilei pentru acces ușor.

Pasul 4 - Validarea token-ului

Trebuie să ne asigurăm că tokenul a fost trimis și este valabil în cazul în care cererea este a POST cerere. Mergeți și introduceți următorul cod în dvs. csrf.php fişier:

/ ** * Validează un token trimis la solicitarea POST. * * @return void * @author Ian Murray * / funcția publică validate_tokens () // Este aceasta o cerere de postare? dacă ($ _SERVER ['REQUEST_METHOD'] == 'POST') // Este setat câmpul token și este valid? $ posted_token = $ this-> CI-> intrare-> post (auto :: $ token_name); dacă $ posted_token === FALSE || $ posted_token! = $ this-> CI-> sesiune-> userdata (self :: $ token_name)) // Cerere nevalidă, trimitere eroare 400. show_error ('Cererea a fost nevalidă. Token-urile nu se potrivesc. ", 400); 
  • Deasupra, validăm cererea, dar numai dacă este a POST cererea, ceea ce înseamnă că, de fapt, a fost depus un formular. Verificăm acest lucru luând o privire la REQUEST_METHOD în cadrul $ _SERVER super global.
  • Verificați dacă jetonul a fost efectiv postat. Dacă ieșirea din $ This-> CI-> introducere de text> post (auto :: $ token_name) este FALS, atunci tokenul nu a fost niciodată postat.
  • Dacă tokenul nu a fost postat sau dacă nu este egal cu cel pe care l-am generat, respingeți solicitarea cu o eroare "Bad Request".

Pasul 5 - Injectați jetoane în vizualizări

Aceasta este partea distractivă! Trebuie să injectăm token-urile în toate formele. Pentru a face viața mai ușoară pentru noi înșine, vom plasa două meta-tag-uri în cadrul nostru (Rails-like). În acest fel, putem include și tokenul în cererile AJAX.

Adăugați următorul cod la adresa dvs. csrf.php fişier:

/ ** * Aceasta injectează etichetele ascunse pe toate formele POST cu jetonul csrf. * De asemenea, injectează meta-anteturile în  de ieșire (dacă există) pentru acces ușor * din cadrele JS. * * @return void * @inutorul Ian Murray * / funcția publică inject_tokens () $ output = $ this-> CI-> output-> get_output (); // Injectați în formă $ output = preg_replace ('/ (<(form|FORM)[^>] * (metoda | METODA) = "(post | POST)" [^>] *>) / ',' $ 0', $ output); // Injectați în  $ output = preg_replace ('/ (<\/head>) / ','". "\ n". '". "\ n". '$ 0', ieșire $); $ This-> CI-> output -> _ afișare ($ output); 
  • Deoarece acesta este a display_override cârlig, trebuie să preluăm ieșirea generată de la CodeIgniter. Noi facem asta folosind $ This-> CI-> bază de obiective> get_output () metodă.
  • Pentru a injecta etichetele în formularele noastre, vom folosi expresii regulate. Expresia asigură faptul că injectăm o etichetă de intrare ascunsă (care conține tokenul generat) numai în formulare cu o metodă de tip POST.
  • De asemenea, trebuie să injectăm meta tagurile noastre în antet (daca prezentul). Acest lucru este simplu, deoarece eticheta capului de închidere ar trebui să fie prezentă doar o dată pe fișier.
  • În sfârșit, din moment ce folosim display_override cârlig, metoda implicită de afișare a vizualizării nu va fi apelată. Această metodă include tot felul de lucruri pe care nu ar trebui să le facem - doar pentru a injecta un cod. Apelarea la noi înșine rezolvă acest lucru.

Pasul 6 - Cârlige

Nu în ultimul rând, trebuie să creăm cârligele înșiși - așa că metodele noastre sunt chemate. Inserați următorul cod în Sistem / aplicație / config / hooks.php fişier:

// // Cârlige de protecție CSRF, nu atingeți acestea decât dacă știți ce faceți //. ORDERUL ACESTEI HOOKS ESTE IMPORTANT EXTREMEL! // ACEASTĂ trebuie să mergeți primul în post -controller_constructor LISTA HOOK. $ hook ['post_controller_constructor'] [] = array (// Mind "[]", acesta nu este singurul post_controller_constructor cârlig 'class' => 'CSRF_Protection', 'function' => 'validate_tokens' > 'csrf.php', 'filepath' => 'cârlige'); // generează tokenul (TREBUIE SĂ FACĂ DUPĂ VALIDARE AU FOST FĂCUT, DUPĂ CĂ CONTROLLERUL ESTE EXECUTAT, ALTUL UTILIZATOR NU ESTE ACCESORIAT LA UN FORMULAR VALUTAT PENTRU FORMULARI VAMALE). $ hook ['post_controller_constructor'] [] = array (// Mind "[]", acesta nu este singurul post_controller_constructor cârlig 'class' => 'CSRF_Protection', ' > 'csrf.php', 'filepath' => 'cârlige'); // Aceasta injectează jetoanele pe toate formele $ hook ['display_override'] = array ('class' => 'CSRF_Protection', 'function' => 'inject_tokens', 'filename' => 'csrf.php' => "cârlige");
  • Primul cârlig îi sună validate_tokens metodă. Aceasta nu este singura post_controller_constructor cârlig, așa că trebuie să adăugăm acele paranteze ("[]"). Consultați documentația privind cârligele CodeIgniter pentru mai multe informații.
  • Cel de-al doilea cârlig, care este de asemenea a post_controller_constructor, generează tokenul în cazul în care nu a fost încă generat.
  • Al treilea este suprascrierea afișajului. Acest cârlig va injecta jetoanele noastre în formă si antet.

Înfășurarea în sus

Cu un efort minim, am construit o bibliotecă destul de bună pentru noi înșine.

Puteți utiliza această bibliotecă în orice proiect și vă va proteja automat site-ul împotriva CSRF.

Dacă doriți să contribuiți la acest mic proiect, vă rugăm să lăsați un comentariu de mai jos sau să dezactivați proiectul pe GitHub. Alternativ, începând cu CodeIgniter v2.0, protecția împotriva atacurilor CSRF este acum integrată în cadrul!

Cod