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:
- 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
- en - tĂȘtes - ont un format de valeur-clĂ© sĂ©parĂ© par deux points, chaque nouveau titre est Ă©crit Ă partir d'une nouvelle ligne
- 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:
- 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
- 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:

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»:

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:

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ùcheAjoutez 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;
- 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
- 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()}
- 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)
- 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ùcheDé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ùcheImplé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ĂącheModifiez 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ùchePour 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:
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- le verrou est libéré à la fin du script
Il est trÚs facile de "coller" à cette erreur, créez deux fichiers:
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();
TĂącheLa 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":