Protecția ID-ului de sesiune în PHP. Capcanele utilizării sesiunilor în PHP Transmiterea unei valori sau a unei matrice folosind o sesiune PHP

Salutări, dragă comunitate.

În primul rând, vreau să vă mulțumesc pentru o resursă foarte utilă. Nu o dată am găsit aici multe idei interesante și sfaturi practice.

Scopul acestui articol este de a evidenția capcanele utilizării sesiunilor în PHP. Desigur, există documentație PHP și o mulțime de exemple, iar acest articol nu se dorește a fi un ghid complet. Este conceput pentru a dezvălui unele dintre nuanțele lucrului cu sesiuni și pentru a proteja dezvoltatorii de pierderi inutile de timp.

Cel mai comun exemplu de utilizare a sesiunilor este, desigur, autorizarea utilizatorului. Să începem cu implementarea cea mai de bază pentru a o dezvolta treptat pe măsură ce apar noi sarcini.

(Pentru a economisi spațiu și timp, ne vom limita exemplele doar la funcțiile de sesiune în sine, în loc să construim aici o aplicație de testare cu drepturi depline, cu o ierarhie de clasă frumoasă, gestionarea completă a erorilor și alte lucruri bune).

Funcția startSession() ( // Dacă sesiunea a fost deja începută, opriți executarea și returnați TRUE // (parametrul session.auto_start din fișierul de setări php.ini trebuie să fie dezactivat - valoarea implicită) if (session_id()) return true; else return session_start(); // Notă: înainte de versiunea 5.3.0, funcția session_start() returna TRUE chiar dacă a apărut o eroare. // Dacă utilizați o versiune anterioară versiunii 5.3.0, efectuați o verificare suplimentară pentru session_id() // după apelarea session_start() ) funcția destroySession() ( if (session_id()) ( // Dacă există o sesiune activă, ștergeți cookie-urile de sesiune, setcookie(session_name(), session_id(), time( )-60*60*24); // și distrug sesiunea session_unset(); session_destroy(); ))

Notă: Se presupune că cititorul are cunoștințe de bază despre sesiunile PHP, așa că nu vom acoperi aici principiul de funcționare al funcțiilor session_start() și session_destroy(). Sarcinile de aspect al formularului de conectare și autentificarea utilizatorului nu au legătură cu subiectul articolului, așa că le vom omite și ele. Permiteți-mi doar să vă reamintesc că pentru a identifica utilizatorul în fiecare solicitare ulterioară, în momentul autentificării cu succes, trebuie să stocăm identificatorul utilizatorului într-o variabilă de sesiune (denumită userid, de exemplu), care va fi disponibilă în toate solicitările ulterioare din cadrul viata sedintei. De asemenea, este necesar să implementăm procesarea rezultatului funcției noastre startSession(). Dacă funcția returnează FALSE, afișați formularul de autentificare în browser. Dacă funcția a returnat TRUE și o variabilă de sesiune care conține identificatorul utilizatorului autorizat (în cazul nostru - userid), există - afișați pagina utilizatorului autorizat (pentru mai multe informații despre tratarea erorilor, consultați adăugarea din 2013-06- 07 în secțiunea privind variabilele de sesiune).

Până acum totul este clar. Întrebările încep atunci când trebuie să implementați controlul inactivității utilizatorului (expirarea sesiunii), să permiteți mai multor utilizatori să lucreze simultan într-un singur browser și, de asemenea, să protejați sesiunile împotriva utilizării neautorizate. Acest lucru va fi discutat mai jos.

Controlul inactivității utilizatorului folosind instrumente PHP încorporate Prima întrebare care apare adesea în rândul dezvoltatorilor de diverse console pentru utilizatori este întreruperea automată a sesiunii în cazul inactivității din partea utilizatorului. Nu este nimic mai ușor decât să faci asta folosind capabilitățile încorporate ale PHP. (Această opțiune nu este deosebit de fiabilă sau flexibilă, dar o vom lua în considerare pentru a fi completă).

Funcția startSession() ( // Timeout inactivitatea utilizatorului (în secunde) $sessionLifetime = 300; dacă (session_id()) returnează true; // Setați durata de viață a cookie-ului ini_set("session.cookie_lifetime", $sessionLifetime); // Dacă utilizatorul timeout de inactivitate este setat, setați durata de viață a sesiunii pe server // Notă: Pentru un server de producție, se recomandă să presetați acești parametri în fișierul php.ini dacă ($sessionLifetime) ini_set("session.gc_maxlifetime", $sessionLifetime) ; if (session_start()) ( setcookie(session_name(), session_id(), time()+$sessionLifetime); returnează adevărat; ) altfel returnează false; )

Câteva precizări. După cum știți, PHP determină ce sesiune trebuie lansată prin numele cookie-ului trimis de browser în antetul cererii. Browserul, la rândul său, primește acest cookie de la server, unde funcția session_start() îl plasează. Dacă cookie-ul browserului a expirat, acesta nu va fi trimis în cerere, ceea ce înseamnă că PHP nu va putea determina ce sesiune să înceapă și va trata aceasta ca pe crearea unei noi sesiuni. Parametrul de setări PHP session.gc_maxlifetime, care este setat egal cu timeout-ul de inactivitate al utilizatorului, stabilește durata de viață a unei sesiuni PHP și este controlat de server. Controlul duratei de viață a sesiunii funcționează după cum urmează (aici considerăm un exemplu de stocare a sesiunilor în fișiere temporare ca fiind cea mai comună și implicită opțiune în PHP).

Când este creată o nouă sesiune, un fișier numit sess_ este creat în directorul setat ca director pentru stocarea sesiunilor în parametrul de setări PHP session.save_path, unde este identificatorul sesiunii. În continuare, în fiecare solicitare, în momentul lansării unei sesiuni deja existente, PHP actualizează ora de modificare a acestui fișier. Astfel, în fiecare cerere ulterioară, PHP, prin diferența dintre ora curentă și ora ultimei modificări a fișierului de sesiune, poate determina dacă sesiunea este activă sau durata de viață a expirat deja. (Mecanismul de ștergere a fișierelor vechi de sesiune este discutat mai detaliat în secțiunea următoare.)

Notă: Trebuie remarcat aici că parametrul session.gc_maxlifetime se aplică tuturor sesiunilor dintr-un singur server (mai precis, în cadrul unui proces PHP principal). În practică, aceasta înseamnă că, dacă pe server rulează mai multe site-uri și fiecare dintre ele are propriul timeout de inactivitate a utilizatorului, atunci setarea acestui parametru pe unul dintre site-uri va duce la setarea lui pentru alte site-uri. Același lucru este valabil și pentru găzduirea partajată. Pentru a evita această situație, sunt utilizate directoare de sesiune separate pentru fiecare site din același server. Setarea căii către directorul de sesiuni se face folosind parametrul session.save_path din fișierul de setări php.ini sau apelând funcția ini_set(). După aceasta, sesiunile fiecărui site vor fi stocate în directoare separate, iar parametrul session.gc_maxlifetime setat pe unul dintre site-uri va fi valabil doar pentru sesiunea acestuia. Nu vom lua în considerare acest caz în detaliu, mai ales că avem o opțiune mai flexibilă pentru monitorizarea inactivității utilizatorilor.

Controlul inactivitatii utilizatorului folosind variabilele de sesiune S-ar părea că opțiunea anterioară, cu toată simplitatea ei (doar câteva linii suplimentare de cod), oferă tot ce avem nevoie. Dar ce se întâmplă dacă nu orice solicitare poate fi considerată ca rezultat al activității utilizatorului? De exemplu, o pagină are un cronometru care face periodic o solicitare AJAX pentru a primi actualizări de la server. O astfel de solicitare nu poate fi privită ca activitate a utilizatorului, ceea ce înseamnă că extinderea automată a duratei de viață a sesiunii nu este corectă în acest caz. Dar știm că PHP actualizează automat ora de modificare a fișierului de sesiune de fiecare dată când este apelată funcția session_start(), ceea ce înseamnă că orice solicitare va duce la o prelungire a duratei de viață a sesiunii, iar timeout-ul de inactivitate al utilizatorului nu va avea loc niciodată. În plus, ultima notă din secțiunea anterioară despre complexitățile parametrului session.gc_maxlifetime poate părea prea confuză și dificil de implementat pentru unii.

Pentru a rezolva această problemă, vom abandona utilizarea mecanismelor PHP încorporate și vom introduce câteva variabile noi de sesiune care ne vor permite să controlăm singuri timpul de inactivitate a utilizatorului.

Funcția startSession($isUserActivity=true) ( ​​​​$sessionLifetime = 300; if (session_id()) return true; // Setați durata de viață a cookie-ului înainte de a închide browserul (vom controla totul pe partea de server) ini_set("session. cookie_lifetime", 0) ; dacă (! session_start()) returnează fals; $t = time(); if ($sessionLifetime) ( // Dacă este setat timeout-ul de inactivitate al utilizatorului, // se verifică timpul scurs de la ultima activitate a utilizatorului // (ora ultimei solicitări când a fost actualizată variabila de sesiune lastactivity) if (isset($_SESSION["lastactivity"]) && $t-$_SESSION["lastactivity"] >= $sessionLifetime) ( // Dacă timpul a trecut de la ultima activitate a utilizatorului, // este mai mare decât expirarea timpului de inactivitate, ceea ce înseamnă că sesiunea a expirat și trebuie să încheiați sesiunea destroySession(); return false; ) else ( // Dacă expirarea nu a avut loc încă, // și dacă cererea a venit ca urmare a activității utilizatorului, // actualizați variabila lastactivity cu valoarea curentului o dată, // extinzând astfel durata sesiunii cu o altă sesiuneLifetime secunde dacă ($isUserActivity) $_SESSION["lastactivity"] = $t; ) ) returnează adevărat; )

Să rezumam. În fiecare cerere, verificăm dacă timeout-ul a fost atins de la ultima activitate a utilizatorului până în momentul actual, iar dacă acesta a fost atins, distrugem sesiunea și întrerupem execuția funcției, returnând FALSE. Dacă expirarea nu a fost atinsă și parametrul $isUserActivity cu valoarea TRUE este transmis funcției, actualizăm ora ultimei activități a utilizatorului. Tot ce trebuie să facem este să stabilim în scriptul de apelare dacă cererea este rezultatul activității utilizatorului și, dacă nu, să apelăm funcția startSession cu parametrul $isUserActivity setat la FALSE.

Adăugare din 2013-06-07 Procesarea rezultatului funcției sessionStart().

Comentariile au subliniat că returnarea FALSE nu oferă o înțelegere completă a cauzei erorii, iar acest lucru este absolut corect. Nu am publicat aici tratarea detaliată a erorilor (lungimea articolului este deja destul de mare), deoarece aceasta nu are legătură directă cu subiectul articolului. Dar având în vedere comentariile, voi clarifica.

După cum puteți vedea, funcția sessionStart poate returna FALSE în două cazuri. Fie sesiunea nu a putut fi pornită din cauza unor erori interne ale serverului (de exemplu, setări incorecte ale sesiunii în php.ini), fie durata de viață a sesiunii a expirat. În primul caz, trebuie să redirecționăm utilizatorul către o pagină cu o eroare care afirmă că există probleme pe server și un formular de contact pentru asistență. În cel de-al doilea caz, trebuie să transferăm utilizatorul în formularul de autentificare și să afișăm în acesta un mesaj corespunzător care să precizeze că sesiunea a expirat. Pentru a face acest lucru, trebuie să introducem coduri de eroare și să returnăm codul corespunzător în loc de FALSE, iar în metoda de apelare, să îl verificăm și să acționăm în consecință.

Acum, chiar dacă o sesiune pe server încă există, aceasta va fi distrusă prima dată când este accesată dacă expirarea timpului de inactivitate a utilizatorului a expirat. Și acest lucru se va întâmpla indiferent de durata de viață a sesiunii setată în setările globale PHP.

Notă: Ce se întâmplă dacă browserul a fost închis și cookie-ul pentru numele sesiunii a fost distrus automat? Solicitarea către server data viitoare când browserul este deschis nu va conține cookie-ul de sesiune, iar serverul nu va putea deschide sesiunea și nu va putea verifica timpul de inactivitate al utilizatorului. Pentru noi, acest lucru este echivalent cu crearea unei noi sesiuni și nu afectează în niciun fel funcționalitatea sau securitatea. Dar apare o întrebare corectă - cine va distruge atunci vechea sesiune, dacă până acum am distrus-o după expirarea timpului de expirare? Sau va rămâne acum în directorul de sesiuni pentru totdeauna? Pentru a curăța sesiunile vechi în PHP, există un mecanism numit colectare de gunoi. Acesta rulează în momentul următoarei solicitări către server și șterge toate sesiunile vechi pe baza ultimei date de modificare a fișierelor de sesiune. Dar mecanismul de colectare a gunoiului nu începe cu fiecare cerere către server. Frecvența (sau mai degrabă, probabilitatea) lansării este determinată de doi parametri de setări session.gc_probability și session.gc_divisor. Rezultatul împărțirii primului parametru la al doilea este probabilitatea lansării mecanismului de colectare a gunoiului. Astfel, pentru ca mecanismul de ștergere a sesiunii să fie lansat cu fiecare cerere către server, acești parametri trebuie setați la valori egale, de exemplu „1”. Această abordare garantează un director de sesiune curat, dar este evident prea costisitoare pentru server. Prin urmare, pe sistemele de producție, valoarea implicită a session.gc_divisor este setată la 1000, ceea ce înseamnă că mecanismul de colectare a gunoiului va rula cu o probabilitate de 1/1000. Dacă experimentați aceste setări în fișierul dvs. php.ini, este posibil să observați că în cazul descris mai sus, atunci când browserul se închide și șterge toate cookie-urile, mai rămân sesiuni vechi în directorul de sesiuni pentru o perioadă. Dar acest lucru nu ar trebui să vă îngrijoreze, pentru că... așa cum sa menționat deja, acest lucru nu afectează în niciun fel siguranța mecanismului nostru.

Actualizare din 2013-06-07 Prevenirea blocării scripturilor din cauza blocării fișierelor de sesiune

Comentariile au ridicat problema înghețarii rulării simultane a scripturilor din cauza blocării fișierului de sesiune (cea mai frapantă opțiune este sondajul lung).

Pentru început, observ că această problemă nu depinde direct de încărcarea serverului sau de numărul de utilizatori. Desigur, cu cât sunt mai multe solicitări, cu atât scripturile sunt executate mai lent. Dar aceasta este o dependență indirectă. Problema apare doar în cadrul unei sesiuni, când serverul primește mai multe solicitări în numele unui utilizator (de exemplu, una dintre ele este un sondaj lung, iar restul sunt solicitări obișnuite). Fiecare cerere încearcă să acceseze același fișier de sesiune, iar dacă cererea anterioară nu a deblocat fișierul, atunci cea ulterioară se va bloca în așteptare.

Pentru a menține blocarea fișierelor de sesiune la minimum, este recomandat să închideți sesiunea apelând funcția session_write_close() imediat după ce toate acțiunile cu variabilele de sesiune au fost finalizate. În practică, aceasta înseamnă că nu ar trebui să stocați totul în variabilele de sesiune și să le accesați pe toată durata execuției scriptului. Și dacă trebuie să stocați unele date de lucru în variabilele de sesiune, citiți-le imediat când începe sesiunea, salvați-le în variabilele locale pentru utilizare ulterioară și închideți sesiunea (adică închiderea sesiunii folosind funcția session_write_close și nu distrugerea acesteia folosind session_destroy ).

În exemplul nostru, aceasta înseamnă că imediat după deschiderea unei sesiuni, verificând durata de viață a acesteia și existența unui utilizator autorizat, trebuie să citim și să salvăm toate variabilele de sesiune suplimentare cerute de aplicație (dacă există), apoi să închidem sesiunea folosind un apel. la session_write_close() și continuă execuția unui script, fie că este un sondaj lung sau o solicitare obișnuită.

Protejarea sesiunilor împotriva utilizării neautorizate Să ne imaginăm o situație. Unul dintre utilizatorii dvs. primește un troian care fură cookie-urile browserului (în care este stocată sesiunea noastră) și îl trimite la adresa de e-mail specificată. Atacatorul obține cookie-ul și îl folosește pentru a falsifica o solicitare în numele utilizatorului nostru autorizat. Serverul acceptă și procesează cu succes această solicitare ca și cum ar proveni de la un utilizator autorizat. Dacă nu este implementată verificarea suplimentară a adresei IP, un astfel de atac va duce la o piratare cu succes a contului utilizatorului cu toate consecințele care decurg.

De ce a fost posibil acest lucru? Evident, pentru că numele și identificatorul de sesiune sunt întotdeauna aceleași pe toată durata de viață a sesiunii, iar dacă primiți aceste date, puteți trimite cu ușurință solicitări în numele altui utilizator (desigur, pe durata de viață a acestei sesiuni). Poate că acesta nu este cel mai comun tip de atac, dar teoretic pare destul de fezabil, mai ales având în vedere că un astfel de troian nici măcar nu are nevoie de drepturi de administrator pentru a jefui cookie-urile browserului utilizatorului.

Cum te poți proteja de astfel de atacuri? Din nou, evident, prin limitarea duratei de viață a identificatorului de sesiune și schimbarea periodică a identificatorului în cadrul aceleiași sesiuni. De asemenea, putem schimba numele sesiunii ștergându-l complet pe cel vechi și creând o nouă sesiune, copiend toate variabilele de sesiune din cea veche în ea. Dar acest lucru nu afectează esența abordării, așa că pentru simplitate ne vom limita doar la identificatorul de sesiune.

Este clar că, cu cât durata de viață a ID-ului sesiunii este mai scurtă, cu atât mai puțin timp va avea un atacator pentru a obține și utiliza cookie-uri pentru a falsifica o solicitare de utilizator. În mod ideal, pentru fiecare solicitare ar trebui folosit un nou identificator, ceea ce va minimiza posibilitatea de a utiliza sesiunea altcuiva. Dar vom lua în considerare cazul general când timpul de regenerare a identificatorului de sesiune este setat arbitrar.

(Vom omite partea din cod care a fost deja discutată).

Funcția startSession($isUserActivity=true) ( ​​​​// Durata de viață a identificatorului de sesiune $idLifetime = 60; ... if ($idLifetime) ( // Dacă durata de viață a identificatorului de sesiune este setată, // verificați timpul scurs de când a fost sesiunea creat sau ultima regenerare // (ora ultimei solicitări când a fost actualizată variabila sesiune starttime) if (isset($_SESSION["starttime"))) ( if ($t-$_SESSION["starttime"] >= $ idLifetime) ( // Timpul în care durata de viață a identificatorului de sesiune a expirat // Generați un nou identificator session_regenerate_id(true); $_SESSION["starttime"] = $t; ) ) else ( // Ajungem aici dacă sesiunea tocmai a fost creat // Setați ora pentru generarea identificatorului de sesiune la ora curentă $_SESSION["starttime"] = $t; ) ) return true; )

Deci, la crearea unei noi sesiuni (care apare atunci când utilizatorul se conectează cu succes), setăm variabila sesiune starttime, care stochează pentru noi ora ultimei generații a identificatorului de sesiune, la o valoare egală cu ora actuală a serverului. În continuare, în fiecare cerere, verificăm dacă a trecut suficient timp (idLifetime) de la ultima generație a identificatorului și, dacă da, generăm unul nou. Astfel, dacă pe durata de viață setată a identificatorului atacatorul care a primit cookie-ul utilizatorului autorizat nu are timp să-l folosească, cererea falsă va fi considerată de către server ca neautorizată, iar atacatorul va fi dus la pagina de autentificare. .

Notă: Noul ID de sesiune intră în cookie-ul browser-ului când este apelată funcția session_regenerate_id(), care trimite noul cookie, similar cu funcția session_start(), deci nu trebuie să actualizăm cookie-ul noi înșine.

Dacă vrem să ne facem sesiunile cât mai sigure, este suficient să setăm durata de viață a identificatorului la unul sau chiar să eliminăm funcția session_regenerate_id() din paranteze și să eliminăm toate verificările, ceea ce va duce la regenerarea identificatorului în fiecare. cerere. (Nu am testat impactul acestei abordări asupra performanței și pot spune doar că funcția session_regenerate_id(true) efectuează în esență doar 4 acțiuni: generarea unui nou identificator, crearea unui antet cu cookie-ul de sesiune, ștergerea celui vechi și crearea un nou fișier de sesiune).

Digresiune lirică: Dacă troianul se dovedește a fi atât de inteligent încât nu trimite cookie-uri atacatorului, ci organizează trimiterea unei cereri false pregătite în prealabil imediat după primirea cookie-ului, metoda descrisă mai sus cel mai probabil nu va putea protejați împotriva unui astfel de atac, deoarece între momentul în care troianul primește cookie-ul și trimiterea unei cereri false nu va exista practic nicio diferență și există o probabilitate mare ca în acest moment identificatorul de sesiune să nu fie regenerat.

Posibilitatea de a lucra simultan într-un singur browser în numele mai multor utilizatori. Ultima sarcină pe care aș dori să o iau în considerare este posibilitatea de a lucra simultan într-un singur browser de către mai mulți utilizatori. Această caracteristică este utilă mai ales în etapa de testare, atunci când trebuie să emulați munca simultană a utilizatorilor și este recomandabil să faceți acest lucru în browserul dvs. preferat, mai degrabă decât să utilizați întregul arsenal disponibil sau să deschideți mai multe instanțe ale browserului în modul incognito. .

În exemplele noastre anterioare, nu am specificat în mod explicit un nume de sesiune, așa că a fost folosit numele PHP implicit (PHPSESSID). Aceasta înseamnă că toate sesiunile pe care le-am creat până acum au trimis un cookie către browser sub numele PHPSESSID. Evident, dacă numele cookie-ului este întotdeauna același, atunci nu există nicio modalitate de a organiza două sesiuni cu același nume în același browser. Dar dacă am folosi propriul nostru nume de sesiune pentru fiecare utilizator, problema ar fi rezolvată. Hai să facem asta.

Funcția startSession($isUserActivity=true, $prefix=null) ( ... dacă (session_id()) returnează adevărat; // Dacă prefixul utilizatorului este trecut în parametri, // setați un nume de sesiune unic care include acest prefix, // altfel setează numele comun pentru toți utilizatorii (de exemplu, MYPROJECT) session_name("MYPROJECT".($prefix ? "_".$prefix: "")); ini_set("session.cookie_lifetime", 0); dacă (! session_start()) returnează false; ... )

Acum tot ce rămâne este să vă asigurați că scriptul de apelare transmite un prefix unic pentru fiecare utilizator funcției startSession(). Acest lucru se poate face, de exemplu, prin trecerea unui prefix în parametrii GET/POST ai fiecărei cereri sau printr-un cookie suplimentar.

Concluzie În concluzie, voi furniza codul final complet al funcțiilor noastre pentru lucrul cu sesiunile PHP, inclusiv toate sarcinile discutate mai sus.

Funcția startSession($isUserActivity=true, $prefix=null) ( $sessionLifetime = 300; $idLifetime = 60; dacă (session_id()) returnează adevărat; session_name("MYPROJECT".($prefix ? "_".$prefix: "")); ini_set("session.cookie_lifetime", 0); if (! session_start()) returnează false; $t = time(); if ($sessionLifetime) ( if (isset($_SESSION["lastactivity"]) ) && $t-$_SESSION["lastactivity"] >= $sessionLifetime) ( destroySession(); return false; ) else ( if ($isUserActivity) $_SESSION["lastactivity"] = $t; ) ) if ($idLifetime) ) ( if (isset($_SESSION["starttime"))) ( if ($t-$_SESSION["starttime"] >= $idLifetime) ( session_regenerate_id(true); $_SESSION["starttime"] = $t; ) ) else ( $_SESSION["starttime"] = $t; ) ) returnează adevărat; ) funcția destroySession() ( if (session_id()) ( session_unset(); setcookie(session_name(), session_id(), time() -60*60*24); session_destroy(); ) )

Sper că acest articol va economisi ceva timp pentru cei care nu au aprofundat niciodată prea adânc în mecanismul de sesiune și să ofere suficientă perspectivă asupra acestui mecanism pentru cei care abia încep să se familiarizeze cu PHP.

Ai nevoie de un nume de utilizator și o parolă?

Pentru a trimite articole online și a verifica starea articolelor trimise, trebuie să vă înregistrați și să vă conectați la contul dvs.

Lista de verificare pentru pregătirea unui articol pentru depunere

Ca parte a procesului de trimitere a articolelor, autorii trebuie să verifice dacă articolul lor îndeplinește toate următoarele puncte; articolele pot fi returnate autorilor dacă nu îndeplinesc aceste cerințe.

Articolul a fost pregătit în conformitate cu cerințele

Condiții de transfer al dreptului de autor

Autorii păstrează drepturile de autor asupra lucrării și acordă revistei drepturi de primă publicare împreună cu lucrarea, în timp ce o licențiază în conformitate cu termenii licenței de atribuire Creative Commons, care permite altora să distribuie această lucrare cu atribuirea obligatorie autorului lucrării și un link. la publicația originală din acest jurnal.

Declarație de confidențialitate

Numele și adresele de e-mail introduse pe site-ul revistei vor fi utilizate exclusiv în scopurile desemnate de această revistă și nu vor fi utilizate în niciun alt scop sau furnizate nici unei alte persoane sau entități.

Înainte de a se înregistra în sistem, utilizatorul este de acord cu politica de prelucrare și stocare a datelor cu caracter personal.

Plăți ale autorului

1500 de caractere cu spații: 300,00 (RUB)

Publicarea a 1 pagină de manuscris (1500 de caractere) - 300 de ruble. Materialele grafice / tabelele sunt plătite separat - 50 de ruble / 1 bucată. Copia autorului, inclusiv transportul în Rusia, se plătește la cererea autorului - 400 de ruble. Livrare în străinătate - 800 de ruble. Costul trimiterii unui certificat de acceptare a materialului pentru publicare este de 150 de ruble.

Traducerea informațiilor însoțitoare (numele complet, locul de muncă al autorilor; titlu; rezumat; cuvinte cheie) în engleză 0,5 ruble pentru fiecare caracter, inclusiv spațiile.

Atenţie! Autorii (candidați și doctori în științe) care, potrivit elibrary.ru, au 300 sau mai multe citări (ponderea autocitărilor nu trebuie să fie mai mare de 30%) sunt publicate gratuit. Dacă sunteți eligibil pentru publicare gratuită, atunci când trimiteți material, în câmpul de comentarii, indicați un link către profilul dvs. de bibliotecă cu numărul de citări. Costurile de transport pentru colectare se plătesc separat.

Securitatea site-ului se bazează pe managementul sesiunilor. Când un utilizator se conectează la un site securizat, acesta oferă acreditări, de obicei sub forma unui nume de utilizator și a unei parole. Serverul web nu are idee care utilizator este deja autentificat sau cum navighează de la o pagină la alta. Mecanismul de sesiune împiedică utilizatorii să introducă o parolă de fiecare dată când doresc să efectueze o nouă acțiune sau să acceseze o pagină nouă.

În esență, gestionarea sesiunii asigură că utilizatorul conectat în prezent este cel care a fost autentificat. Dar, din păcate, sesiunile au devenit o țintă evidentă pentru hackeri, deoarece pot permite accesul la un server web fără a fi nevoie de autentificare.

După ce utilizatorul este autentificat, serverul web îi oferă un ID de sesiune. Acest ID este stocat în browser și este înlocuit ori de câte ori este nevoie de autentificare. Acest lucru vă permite să evitați procesele repetate de autentificare/introducere a parolei. Toate acestea se întâmplă în fundal și nu provoacă disconfort utilizatorului. Imaginează-ți dacă ai introduce numele de utilizator și parola de fiecare dată când ai vizualizat o pagină nouă!

În acest articol voi încerca să subliniez toate modalitățile pe care le cunosc pentru a proteja ID-ul sesiunii în PHP.

Utilizarea cookie-urilor În mod implicit, toate informațiile despre sesiune, inclusiv ID-ul, sunt trimise la un cookie. Dar asta nu se întâmplă întotdeauna. Unii utilizatori dezactivează cookie-urile în browserele lor. În acest caz, browserul va transmite ID-ul sesiunii în adresa URL.

Aici ID-ul este transmis în text clar, spre deosebire de o sesiune prin intermediul unui cookie, când informația este ascunsă în antetul HTTP. Cel mai simplu mod de a vă proteja împotriva acestui lucru ar fi interzicerea transmiterii identificatorului de sesiune prin bara de adrese. Acest lucru se poate face scriind următoarele în fișierul de configurare .htaccess server Apache:

Php_flag session.use_only_cookies activat

Utilizarea criptării Dacă site-ul dvs. trebuie să proceseze informații sensibile, cum ar fi numerele de card de credit (bună ziua de la Sony), ar trebui să utilizați criptarea SSL3.0 sau TSL1.0. Pentru a face acest lucru, atunci când setați un cookie, trebuie să specificați true pentru parametrul securizat.

Dacă stocați parola sesiunii în variabila $_SESSION (este mai bine să utilizați SQL), atunci nu ar trebui să o stocați în text clar.

Dacă ($_SESSION["parolă"] == $userpass) ( // cod )

Codul de mai sus nu este sigur deoarece parola este stocată ca text simplu într-o variabilă de sesiune. În schimb, utilizați criptarea md5, ceva de genul acesta:

Dacă ($_SESSION["md5password"] == md5($userpass)) ( // cod )

Verificare browser Pentru a preveni posibilitatea de a utiliza o sesiune din alt browser (calculator), ar trebui să introduceți o verificare a câmpului antet HTTP user-agent:

Session_start(); if (isset($_SESSION["HTTP_USER_AGENT"])) ( if ($_SESSION["HTTP_USER_AGENT"] != md5($_SERVER["HTTP_USER_AGENT"])) ( // cod ) ) else ( $_SESSION["HTTP_USER_AGENT"" ] = md5($_SERVER["HTTP_USER_AGENT"]); )

Expirarea sesiunii Limitați durata de viață a sesiunii, precum și timpul de expirare a cookie-urilor. În mod implicit, durata sesiunii este de 1440 de secunde. Puteți modifica această valoare prin php.ini și .htaccess. Exemplu pentru .htaccess:

# Durata de viață a sesiunii în secunde
php_value session.gc_maxlifetime 3600
# Durata de viață a cookie-urilor în secunde
php_value session.cookie_lifetime 3600

Legarea după adresa IP În anumite situații (nu întotdeauna), ar trebui să vă legați după adresa IP. În principal, atunci când numărul de utilizatori este limitat și au IP-uri statice. Verificarea se poate baza fie pe lista de adrese IP permise,

Include("ip_list.php"); //$ip_white_list = matrice ("admin1" => "111.222.333.444", "admin2" => "555.666.777.888"); if(!empty(array_search($_SERVER["REMOTE_ADDR"],$ip_white_list))) ( header("Locație: admin.php"); ) else ( echo "ACCESS DENY!"; )

Sau după adresa IP pentru fiecare solicitare (numai pentru IP statică):

If(isset($_SESSION["ip"]) și $_SESSION["ip"] == $_SERVER["REMOTE_ADDR"]) ( header("Locație: admin.php"); ) else ( session_unset(); $ _SESSION[„ip”] = $_SERVER[„REMOTE_ADDR”]; )

Trebuie să știți că hacking-ul nu poate fi evitat complet. Puteți face acest hack cât mai dificil cu putință prin orice mijloace cunoscute. Cu toate acestea, nu trebuie să uitați nici de utilizatorii dvs. legali, pentru a nu le complica viața cu o astfel de protecție.

Acest articol a fost scris în 2009 și rămâne unul dintre cele mai populare postări ale noastre. Dacă doriți să aflați mai multe despre PHP și MySQL, s-ar putea să găsiți acest lucru de mare interes.

NOTĂ: Acest articol a fost recent actualizat pentru a funcționa pe PHP 4.2 sau o versiune ulterioară!

Recent, am avut ocazia să lucrez la un mic proiect cu un grup de oameni. Am stabilit devreme că doar acel e-mail nu va fi suficient pentru a-i ține pe toți la curent, așa că am fost însărcinat să construiesc un mic site web pentru proiect. Ar conține un simplu panou de mesaje, un loc în care am putea încărca documente și alte fișiere pentru a le utiliza restul echipei și informații de contact pentru diferiții membri ai echipei.

Pentru ca multe dintre aceste funcții să funcționeze, știam că aveam nevoie de utilizatori să se conecteze înainte de a accesa părțile relevante ale site-ului. Ceea ce aveam nevoie era un sistem care să permită utilizatorilor să se înregistreze pentru un ID de utilizator pentru a accesa site-ul, apoi să utilizeze imediat acel ID fără nicio intervenție din partea mea.

În acest articol, voi oferi o privire de ansamblu asupra sistemului pe care l-am dezvoltat, începând din prima jumătate cu procesul de înscriere a utilizatorului. În a doua jumătate, mă voi concentra asupra site-ului în sine, asupra modului în care solicită utilizatorilor să se conecteze și apoi menține starea de autentificare pe tot parcursul vizitei lor. Voi acorda o atenție deosebită utilizării funcțiilor de gestionare a sesiunii în PHP. Până la sfârșit, ar trebui să aveți toate informațiile de care aveți nevoie pentru a implementa un sistem similar al dvs.

Pe parcursul acestui articol, voi presupune că aveți o familiaritate de bază cu limbajul PHP, utilizarea formularelor pentru a trimite informații la un script PHP și modul în care PHP poate fi utilizat pentru a interacționa cu o bază de date MySQL. Dacă oricare dintre acestea vă sunt concepte străine, ar trebui să începeți prin a citi articolul meu anterior, .

Partea întâi: Procesul de înscriere Formularul de înscriere

Un loc natural pentru a începe construirea unui site care va solicita utilizatorilor să se înregistreze pentru acces este procesul de înregistrare în sine. După cum s-ar putea aștepta, un formular simplu bazat pe Web va face trucul. Iată cum va arăta:

Și iată codul pentru acest formular:




Înregistrarea unui nou utilizator



Formular de înregistrare a unui nou utilizator

* indică un câmp obligatoriu


Cu obiectivul clar acum, vă voi ghida prin codul pentru accesscontrol.php . Începeți prin a include cele două fișiere includ la îndemână: