PHP pour les débutants. La session

ElePHPant. PHP pour les débutants. Séance

Passez une bonne journée. Voici le premier article de la série PHP pour les développeurs débutants. Ce sera une série d'articles inhabituelle, il n'y aura pas d' echo "Hello World" , il y aura du hardcore de la vie des programmeurs PHP avec un petit mélange de "devoirs" pour consolider le matériel.

Je vais commencer par les sessions - c'est l'un des composants les plus importants avec lesquels vous devez travailler. Ne pas comprendre les principes de son travail - faire des affaires. Donc, pour éviter les problÚmes, je vais essayer de parler de toutes les nuances possibles.

Mais pour commencer, pour comprendre pourquoi nous avons besoin d'une session, nous nous tournons vers les origines - vers le protocole HTTP.

Protocole HTTP


Le protocole HTTP est HyperText Transfer Protocol - le «Hypertext Transfer Protocol» - c'est-à-dire en fait - un protocole de texte, et comprendre qu'il n'est pas difficile.
Initialement, il Ă©tait entendu que sous ce protocole, seul le HTML sera transmis, l'adresse et le nom, mais maintenant ils n'enverront plus et = ^. ^ = Et ïŒˆâ€ą _ ㅅ _ â€ąïŒ‰

Afin de ne pas tourner autour du pot, permettez-moi de vous donner un exemple de communication via le protocole HTTP.
Voici un exemple de la demande envoyée par votre navigateur lorsque vous demandez la page http://example.com :

 GET / HTTP/1.1 Host: example.com Accept: text/html < > 

Et voici un exemple de réponse:

 HTTP/1.1 200 OK Content-Length: 1983 Content-Type: text/html; charset=utf-8 <html> <head>...</head> <body>...</body> </html> 

Ce sont des exemples trĂšs simplifiĂ©s, mais mĂȘme ici, vous pouvez voir en quoi consistent la demande et la rĂ©ponse HTTP:

  1. ligne de départ - pour une demande contient la méthode et le chemin de la page demandée, pour une réponse - la version du protocole et le code de réponse
  2. en - tĂȘtes - ont un format de valeur-clĂ© sĂ©parĂ© par deux points, chaque nouveau titre est Ă©crit Ă  partir d'une nouvelle ligne
  3. corps du message - soit directement HTML, soit les donnĂ©es sont sĂ©parĂ©es des en-tĂȘtes par deux sauts de ligne, peuvent ĂȘtre absentes, comme dans la demande ci-dessus

Donc, nous avons en quelque sorte compris le protocole - c'est simple, il mÚne son histoire depuis 1992, donc vous ne le nommerez pas idéal, mais ce qu'il est - envoyer une demande - obtenir une réponse, et c'est tout, le serveur et le client ne sont plus connectés. Mais un tel scénario n'est en aucun cas le seul possible, nous pouvons avoir une autorisation, le serveur doit en quelque sorte comprendre que cette demande provient d'un utilisateur spécifique, c'est-à-dire le client et le serveur doivent communiquer au sein d'une certaine session. Et oui, le mécanisme suivant a été inventé pour cela:

  1. Lorsqu'un utilisateur autorise, le serveur génÚre et se souvient d'une clé unique - l'identifiant de session, et le signale au navigateur
  2. Le navigateur enregistre cette clé et, à chaque demande ultérieure, il envoie

Pour mettre en Ɠuvre ce mĂ©canisme, des cookies ont Ă©tĂ© créés (cookies, cookies) - de simples fichiers texte sur votre ordinateur, par fichier pour chaque domaine (bien que certains navigateurs soient plus avancĂ©s et utilisent une base de donnĂ©es SQLite pour stocker), tandis que le navigateur impose une limite sur le nombre d'enregistrements et la taille des donnĂ©es stockĂ©es (pour la plupart des navigateurs, c'est 4096 octets, voir RFC 2109 de 1997)
C'est-Ă -dire si vous volez un cookie de votre navigateur, pouvez-vous aller sur votre page facebook en votre nom? Ne vous inquiĂ©tez pas, cela ne peut pas ĂȘtre fait, au moins avec Facebook, et je vais ensuite vous montrer l'une des façons possibles de vous protĂ©ger contre ce type d'attaque contre vos utilisateurs.

Voyons maintenant comment notre demande-réponse change, soyez-y autorisé:

Demande
 POST /login/ HTTP/1.1 Host: example.com Accept: text/html login=Username&password=Userpass 


Notre mĂ©thode est devenue POST, et le login et le mot de passe sont transmis dans le corps de la requĂȘte. Si vous utilisez la mĂ©thode GET, la chaĂźne de requĂȘte contiendra un nom d'utilisateur et un mot de passe, qui ne sont pas trĂšs corrects d'un point de vue idĂ©ologique, et a un certain nombre d'effets secondaires sous forme de journalisation (par exemple, dans le mĂȘme access.log ) et de mise en cache des mots de passe sous forme claire.

Réponse
 HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 Set-Cookie: KEY=VerySecretUniqueKey <html> <head>...</head> <body>...</body> </html> 

La rĂ©ponse du serveur contiendra l'en Set-Cookie: KEY=VerySecretUniqueKey tĂȘte Set-Cookie: KEY=VerySecretUniqueKey , ce qui forcera le navigateur Ă  enregistrer ces donnĂ©es dans les cookies, et la prochaine fois qu'il accĂ©dera au serveur, il sera envoyĂ© et reconnu par le serveur:

Demande
 GET / HTTP/1.1 Host: example.com Accept: text/html Cookie: KEY=VerySecretUniqueKey < > 

Comme vous pouvez le voir, les en-tĂȘtes envoyĂ©s par le navigateur (en-tĂȘtes de demande) et le serveur (en-tĂȘtes de rĂ©ponse) sont diffĂ©rents, bien qu'ils soient communs aux demandes et aux rĂ©ponses (en-tĂȘtes gĂ©nĂ©raux)

Le serveur a reconnu notre utilisateur par le cookie envoyé et lui donnera en outre accÚs aux informations personnelles. Donc, enfin, triez les sessions et HTTP, vous pouvez maintenant revenir à PHP et à ses fonctionnalités.

PHP et session


J'espĂšre que PHP est dĂ©jĂ  installĂ© sur votre ordinateur, car De plus, je donnerai des exemples, et ils devront ĂȘtre exĂ©cutĂ©s

Le langage PHP a Ă©tĂ© créé pour correspondre au protocole HTTP - c'est-Ă -dire Sa tĂąche principale est de donner une rĂ©ponse Ă  la requĂȘte HTTP et de "mourir" en libĂ©rant de la mĂ©moire et des ressources. Par consĂ©quent, le mĂ©canisme de session ne fonctionne pas en PHP en mode automatique, mais en mode manuel, et vous devez savoir quoi appeler et dans quel ordre.
Ici vous avez un article sur le sujet PHP est destiné à mourir , ou ici c'est en russe , mais il vaut mieux le mettre dans les signets "pour plus tard".

Tout d'abord, vous devez "démarrer" la session - pour cela, nous utilisons la fonction session_start () , créez un fichier session.start.php avec le contenu suivant:

 <?php session_start(); 

Exécutez le serveur Web PHP intégré dans le dossier avec votre script:

 php -S 127.0.0.1:8080 

Lancez le navigateur et ouvrez les outils de dĂ©veloppement (ou autre ), puis allez sur la page http://127.0.0.1:8080/session.start.php - vous ne devriez voir qu'une page vierge, mais ne vous prĂ©cipitez pas pour fermer - regardez aux en-tĂȘtes que le serveur nous a envoyĂ©s:

Cookie

Il y aura beaucoup de choses, nous ne sommes intéressés par cette ligne que dans la réponse du serveur (nettoyer les cookies, s'il n'y en a pas, et rafraßchir la page):

 Set-Cookie: PHPSESSID=dap83arr6r3b56e0q7t5i0qf91; path=/ 

En voyant cela, le navigateur enregistrera un cookie nommé «PHPSESSID»:

Cookie de session du navigateur

PHPSESSID - le nom de session par dĂ©faut, il est ajustĂ© Ă  partir de la configuration php.ini avec la directive session.name , si nĂ©cessaire, le nom peut ĂȘtre changĂ© dans le fichier de configuration lui-mĂȘme ou en utilisant la fonction session_name ()

Et maintenant - nous actualisons la page, et nous voyons que le navigateur envoie ce cookie au serveur, vous pouvez essayer de rafraßchir la page plusieurs fois, le résultat sera identique:

Demande de navigateur avec cookie

Le total que nous avons - la théorie a coïncidé avec la pratique, et c'est trÚs bien.

L'étape suivante consiste à enregistrer une valeur arbitraire dans la session, pour cela, la variable super-globale $_SESSION utilisée en PHP, nous allons enregistrer l'heure actuelle - pour ce faire, appelez la fonction date () :

 session_start(); $_SESSION['time'] = date("H:i:s"); echo $_SESSION['time']; 

Nous actualisons la page et voyons l'heure du serveur, mettons à jour à nouveau - et l'heure a été mise à jour. Assurons-nous maintenant que l'heure définie ne change pas à chaque actualisation de la page:

 session_start(); if (!isset($_SESSION['time'])) { $_SESSION['time'] = date("H:i:s"); } echo $_SESSION['time']; 

Nous mettons Ă  jour - le temps ne change pas, ce qui est nĂ©cessaire. Mais en mĂȘme temps, nous nous souvenons que PHP est en train de mourir, ce qui signifie qu'il stocke cette session quelque part, et nous trouverons cet endroit ...

Tout secret devient clair


Par dĂ©faut, PHP stocke la session dans des fichiers - la directive session.save_handler est responsable de cela, recherchez le chemin oĂč les fichiers sont enregistrĂ©s dans la directive session.save_path , ou utilisez la fonction session_save_path () pour obtenir le chemin nĂ©cessaire.
Dans votre configuration, le chemin d'accĂšs aux fichiers peut ne pas ĂȘtre spĂ©cifiĂ©, puis les fichiers de session seront stockĂ©s dans des fichiers temporaires de votre systĂšme - appelez la fonction sys_get_temp_dir () et dĂ©couvrez oĂč se trouve cet endroit cachĂ©.

Donc, nous sess_dap83arr6r3b56e0q7t5i0qf91 ce chemin et trouvons votre fichier de session (j'ai ce fichier sess_dap83arr6r3b56e0q7t5i0qf91 ), ouvrez-le dans un éditeur de texte:

 time|s:8:"16:19:51"; 

Comme vous pouvez le voir, ici c'est notre heure, c'est le format délicat dans lequel notre session est stockée, mais nous pouvons faire des changements, changer l'heure, ou nous pouvons simplement entrer n'importe quelle ligne, pourquoi pas:

 time|s:13:"\m/ (@.@) \m/"; 

Pour convertir cette chaĂźne en un tableau, vous devez utiliser la fonction session_decode () , pour la conversion inverse - session_encode () - cela s'appelle la sĂ©rialisation, uniquement en PHP pour les sessions - c'est la sienne - spĂ©ciale, bien que vous puissiez utiliser la sĂ©rialisation PHP standard - Ă©crivez dans la directive de configuration de session La valeur .serialize_handler est php_serialize et vous serez satisfait, et $_SESSION peut ĂȘtre utilisĂ© sans restrictions - vous pouvez maintenant utiliser des chiffres et des caractĂšres spĂ©ciaux comme index | et ! au nom (pour tous les 10+ ans de travail, je n'ai jamais eu Ă  le faire :)

TĂąche
Écrivez votre fonction, similaire en fonction Ă  session_decode() , ici vous avez un ensemble de donnĂ©es de test pour la session (il n'est pas nĂ©cessaire de rĂ©soudre la connaissance des expressions rĂ©guliĂšres), prenez le texte Ă  convertir du fichier de votre session actuelle:

 $_SESSION['integer var'] = 123; $_SESSION['float var'] = 1.23; $_SESSION['octal var'] = 0x123; $_SESSION['string var'] = "Hello world"; $_SESSION['array var'] = array('one', 'two', [1,2,3]); $object = new stdClass(); $object->foo = 'bar'; $object->arr = array('hello', 'world'); $_SESSION['object var'] = $object; $_SESSION['integer again'] = 42; 


Alors qu'avons-nous pas encore essayĂ©? C'est vrai - pour voler des cookies, lançons un autre navigateur et y ajoutons les mĂȘmes cookies. Pour cela, j'ai Ă©crit un simple javascript pour vous, copiez-le dans la console du navigateur et exĂ©cutez-le, n'oubliez pas de changer l'identifiant de session en votre propre:

 javascript:(function(){document.cookie='PHPSESSID=dap83arr6r3b56e0q7t5i0qf91;path=/;';window.location.reload();})() 

Maintenant, vos deux navigateurs regardent la mĂȘme session. J'ai mentionnĂ© ci-dessus que je parlerai des mĂ©thodes de protection, considĂ©rons le moyen le plus simple - nous lierons la session au navigateur, plus prĂ©cisĂ©ment, Ă  la façon dont le navigateur apparaĂźt au serveur - nous nous souviendrons de l' agent utilisateur et le vĂ©rifierons Ă  chaque fois:

 session_start(); if (!isset($_SESSION['time'])) { $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT']; $_SESSION['time'] = date("H:i:s"); } if ($_SESSION['ua'] != $_SERVER['HTTP_USER_AGENT']) { die('Wrong browser'); } echo $_SESSION['time']; 

Il est plus difficile de simuler, mais il est toujours possible d'ajouter ici la sauvegarde et la vérification de $_SERVER['REMOTE_ADDR'] et $_SERVER['HTTP_X_FORWARDED_FOR'] , et cela ressemblera plus ou moins à une protection contre les intrus qui empiÚtent sur nos cookies.

Le mot-clé du paragraphe précédent semble que les cookies fonctionnent depuis longtemps sur le protocole HTTPS dans des projets réels, donc personne ne peut les voler sans accÚs physique à votre ordinateur ou smartphone


Il convient de mentionner la directive session.cookie-httponly , grùce à elle le cookie de session sera inaccessible depuis JavaScript. De plus, si vous regardez le manuel de fonction setcookie () , vous remarquerez que le dernier paramÚtre est également responsable de HttpOnly. N'oubliez pas que ce paramÚtre vous permet de gérer efficacement les attaques XSS dans presque tous les navigateurs .

TĂąche
Ajoutez une vérification à l'adresse IP de l'utilisateur dans le code; si la vérification échoue, supprimez la session compromise.

Pas Ă  pas


Et maintenant, je vais expliquer par étapes l'algorithme comment fonctionne une session en PHP, en utilisant le code suivant comme exemple (paramÚtres par défaut):

 session_start(); $_SESSION['id'] = 42; 

  1. aprÚs avoir appelé session_start() PHP recherche dans le cookie l'identifiant de session par le nom spécifié dans session.name - c'est PHPSESSID
  2. s'il n'y a pas d'identifiant, il est créé (voir session_id () ) et crĂ©e un fichier de session vide le long du chemin session.save_path avec le nom sess_{session_id()} , des en-tĂȘtes seront ajoutĂ©s Ă  la rĂ©ponse du serveur pour dĂ©finir le cookie {session_name()}={session_id()}
  3. si l'identifiant est présent, recherchez le fichier de session dans le dossier session.save_path :
    • nous ne le trouvons pas - nous crĂ©ons un fichier vide avec le nom sess_{$_COOKIE[session_name()]} (l'identifiant ne peut contenir que des caractĂšres des plages az , AZ , 0-9 , une virgule et un signe moins)
    • rechercher, lire le fichier et dĂ©compresser les donnĂ©es (voir session_decode () ) dans la variable super globale $_SESSION (le fichier est verrouillĂ© pour la lecture / l'Ă©criture)
  4. lorsque le script a terminé son travail, toutes les données de $_SESSION compressées à l'aide de session_encode() dans un fichier le long du chemin session.save_path nommé sess_{session_id()} (le verrou est libéré)

TĂąche
Définissez une valeur de cookie arbitraire dans votre navigateur avec le nom PHPSESSID , que ce soit 1234567890 , actualisez la page, vérifiez que vous avez créé un nouveau fichier sess_1234567890

Y a-t-il une vie sans cookies?


PHP peut fonctionner avec la session mĂȘme si les cookies sont dĂ©sactivĂ©s dans le navigateur, mais alors toutes les URL sur le site contiendront un paramĂštre avec l'identifiant de votre session, et oui - vous devez toujours le configurer, mais en avez-vous besoin? Je n'ai pas eu Ă  l'utiliser, mais si je le veux vraiment, je vais juste dire oĂč creuser:


Et si vous avez besoin de stocker une session dans une base de données?


Pour stocker une session dans la base de donnĂ©es, vous devrez modifier le magasin de sessions et indiquer Ă  PHP comment l'utiliser. À cet effet, l'interface SessionHandlerInterface et la fonction session_set_save_handler ont Ă©tĂ© créées.
SĂ©parĂ©ment, je note que vous n'avez pas besoin d'Ă©crire vos propres gestionnaires de session pour redis et memcache - lorsque vous installez ces extensions, les gestionnaires correspondants les accompagnent Ă©galement, donc RTFM est tout. Eh bien et oui, le gestionnaire doit ĂȘtre spĂ©cifiĂ© avant d'appeler session_start() ;)

TĂąche
Implémentez SessionHandlerInterface pour stocker la session dans MySQL, vérifiez si cela fonctionne.
Il s'agit d'une tùche avec astérisque pour ceux qui se sont déjà familiarisés avec les bases de données.


Quand la session se termine-t-elle?


La directive session.gc_maxlifetime est responsable de la durĂ©e de vie d'une session. Par dĂ©faut, cette directive est Ă©gale Ă  1440 secondes (24 minutes), il faut comprendre que si une session n'a pas Ă©tĂ© accĂ©dĂ©e pendant un temps spĂ©cifiĂ©, alors la session sera considĂ©rĂ©e comme "pourrie" et attendra son tour pour ĂȘtre supprimĂ©e.

Une autre question est intéressante, pouvez-vous la poser aux développeurs matures - quand PHP supprime-t-il les fichiers des sessions expirées? La réponse se trouve dans le guide officiel, mais pas explicitement - alors souvenez-vous:

Le garbage collection peut ĂȘtre dĂ©marrĂ© lorsque la fonction session_start() est appelĂ©e, la probabilitĂ© de dĂ©marrage dĂ©pend de deux directives session.gc_probability et session.gc_divisor , la premiĂšre agit comme un dividende, la seconde agit comme un diviseur, et par dĂ©faut ces valeurs sont 1 et 100, etc. e. la probabilitĂ© que le collecteur soit lancĂ© et que les fichiers de session soient supprimĂ©s est d'environ 1%.

TĂąche
Modifiez la valeur de la directive session.gc_divisor pour que le garbage collector démarre à chaque fois, vérifiez que cela se produit.


L'erreur la plus banale


Une erreur avec plus d'un demi-million de résultats dans les résultats de Google:

Impossible d'envoyer un cookie de session - en - tĂȘtes dĂ©jĂ  envoyĂ©s par
Impossible d'envoyer le limiteur de cache de session - en - tĂȘtes dĂ©jĂ  envoyĂ©s

Pour en obtenir un, créez un fichier session.error.php avec le contenu suivant:

 echo str_pad(' ', ini_get('output_buffering')); session_start(); 

Dans la deuxiÚme ligne, une étrange «magie» est un focus avec un tampon de sortie, je vais en parler dans l'un des articles suivants, jusqu'à présent, considérez que ce n'est qu'une chaßne de 4096 caractÚres, dans ce cas, ce sont tous les espaces

Commencez par supprimer le cookie au prĂ©alable et vous obtiendrez les erreurs ci-dessus, bien que le texte de l'erreur soit diffĂ©rent, mais l'essence est la mĂȘme: le train est parti - le serveur a dĂ©jĂ  envoyĂ© le contenu de la page au navigateur et il est trop tard pour envoyer les en-tĂȘtes, cela ne fonctionnera pas dans les cookies et l'identifiant de session chĂ©ri n'apparaĂźt pas dans les cookies. Si vous rencontrez cette erreur - recherchez un endroit oĂč le texte est affichĂ© Ă  l'avance, il peut s'agir d'un espace avant les caractĂšres <?php , ou aprĂšs ?> Dans l'un des fichiers connectĂ©s, et bien, s'il s'agit d'un espace, il peut y avoir un fil qui n'est pas imprimable comme BOM , alors soyez prudent, et cette infection ne vous affectera pas (aprĂšs tout ... rire homĂ©rique).

TĂąche
Pour tester ces connaissances, je veux que vous implémentiez votre propre mécanisme de session et que le code ci-dessus fonctionne:

 require_once 'include/sess.php'; sess_start(); if (isset($_SESS["id"])) { echo $_SESS["id"]; } else { $_SESS["id"] = 42; } 

Pour implémenter votre plan, vous aurez besoin de la fonction register_shutdown_function ()



Verrouiller


Une autre erreur courante chez les débutants est une tentative de lecture du fichier de session alors qu'il est verrouillé par un autre script. En fait, ce n'est pas tout à fait une erreur, c'est une mauvaise compréhension du principe de blocage :)

Mais reprenons les étapes:

  1. session_start() non seulement crée / lit un fichier, mais le verrouille également afin que personne ne puisse apporter de modifications au moment de l'exécution du script, ou lire des données non cohérentes dans le fichier de session
  2. le verrou est libéré à la fin du script


Il est trÚs facile de "coller" à cette erreur, créez deux fichiers:

 // start.php session_start(); echo "OK"; 


 // lock.php session_start(); sleep(10); echo "OK"; 


Maintenant, si vous ouvrez la page lock.php dans le navigateur, puis ouvrez start.php dans un nouvel onglet, vous verrez que la deuxiÚme page ne s'ouvrira qu'aprÚs l'exécution du premier script, qui bloque le fichier de session pendant 10 secondes.

Il existe quelques options pour éviter un tel phénomÚne - «maladroit» et «réfléchi».

"Hache"
Utilisez un gestionnaire de session personnalisé dans lequel "oublier" pour implémenter le verrouillage :)
Une option lĂ©gĂšrement meilleure consiste Ă  prendre une clĂ© prĂȘte Ă  l'emploi et Ă  dĂ©sactiver le verrouillage (par exemple, memcached a une telle option - memcached.sess_locking ) O_o
Passez des heures à déboguer du code à la recherche d'une rare erreur de pop-up ...

"Réfléchi"
OĂč est le meilleur moyen - pour surveiller vous-mĂȘme le verrouillage de la session et le retirer lorsqu'il n'est pas nĂ©cessaire:

- Si vous ĂȘtes sĂ»r de ne pas avoir Ă  modifier les donnĂ©es de session, utilisez l'option read_and_close lors du dĂ©marrage de la session:

 session_start([ 'read_and_close' => true ]); 


Ainsi, le verrou sera libéré immédiatement aprÚs la lecture des données de session.

- Si vous devez encore apporter des modifications à la session, aprÚs les avoir fermées, l'enregistrement de la session:

 session_start(); // some changes session_write_close(); 


TĂąche
La liste des deux fichiers start.php et lock.php était un peu plus élevée. Créez davantage read-close.php et write-close.php , dans lesquels vous contrÎlerez le verrouillage de la maniÚre indiquée. Vérifiez comment le verrou fonctionne (ou ne fonctionne pas).


En conclusion


Dans cet article, vous avez reçu sept tùches, alors qu'elles concernent non seulement le travail avec les sessions , mais également la présentation de MySQL et des fonctions de chaßne . Pour l'assimilation de ce matériel - vous n'avez pas besoin d'un article séparé, seul le manuel sur les liens fournis suffit - personne ne le lira pour vous. Allez-y!

PS Si vous avez appris quelque chose de nouveau de l'article - merci à l'auteur - partagez l'article sur les réseaux sociaux;)
PPS Oui, il s'agit d'un article cross-post de mon blog , mais il est toujours pertinent :)

Une série d'articles "PHP pour débutants":

Source: https://habr.com/ru/post/fr437972/


All Articles