Maison >développement back-end >tutoriel php >Cet étrange code PHP dans les frameworks et les CMS
Remarque : Pour suivre cet article, il est supposé que vous avez des connaissances de base en programmation en PHP.
Cet article traite d'un extrait de code PHP que vous avez probablement vu en haut de votre CMS ou framework préféré. Vous avez probablement lu que vous devez toujours l'inclure au début de chaque fichier PHP que vous développez, pour des raisons de sécurité, mais sans explication très claire de pourquoi. Je fais référence à ce code :
<?php if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly }
Ce type de code est très courant dans les fichiers WordPress, bien qu'il apparaisse dans presque tous les frameworks et CMS. Dans le cas du CMS Joomla, par exemple, le seul changement est qu'au lieu d'ABSPATH, il utilise JEXEC. A part ça, la logique reste la même. Ce CMS a évolué à partir d'un système précédent appelé Mambo, qui utilisait également un code similaire, mais avec _VALID_MOS comme constante. Si l'on remonte encore plus loin, on constate que le premier CMS à utiliser ce genre de code était PHP-Nuke (considéré par certains comme le premier CMS basé sur PHP).
Le flux d'exécution de PHP-Nuke (et de la plupart des CMS et frameworks aujourd'hui) consistait à charger séquentiellement plusieurs fichiers pour répondre à l'action que l'utilisateur ou le visiteur avait entreprise sur le site Web. Par exemple, imaginez un site Web de cette époque hébergé sur example.net avec ce CMS installé. Chaque fois que la page d'accueil était chargée, le système exécutait une séquence de fichiers dans l'ordre (ceci n'est qu'un exemple, pas une séquence réelle) : index.php => load_modules.php => modules.php. Dans cette chaîne, index.php a été chargé en premier, qui a ensuite chargé load_modules.php, qui à son tour a chargé modules.php.
Cette chaîne d'exécution ne commençait pas toujours par le premier fichier (index.php). En fait, n'importe qui pourrait contourner une partie du flux en accédant directement à l'un des autres fichiers PHP via son URL (par exemple, http://example.net/load_modules.php ou http://example.net/modules.php) , ce qui, comme nous le verrons, pourrait être risqué dans de nombreux cas.
Comment ce problème a-t-il été résolu ? Une mesure de sécurité a été introduite, ajoutant un code similaire à celui-ci au début de chaque fichier :
<?php if (!eregi("modules.php", $HTTP_SERVER_VARS['PHP_SELF'])) { die ("You can't access this file directly..."); }
Essentiellement, ce code, placé en haut d'un fichier nommé modules.php, vérifiait si modules.php était accessible directement via l'URL. Si tel était le cas, l'exécution était arrêtée, affichant le message : « Vous ne pouvez pas accéder directement à ce fichier… » Si $HTTP_SERVER_VARS['PHP_SELF'] ne contenait pas modules.php, cela signifiait que le flux d'exécution normal était actif, permettant au script pour continuer.
Ce code présentait cependant certaines limites. Premièrement, le code était différent pour chaque fichier dans lequel il était inséré, ce qui ajoutait à la complexité. De plus, dans certaines situations, PHP n'attribuait pas de valeur à $HTTP_SERVER_VARS['PHP_SELF'], ce qui limitait son efficacité.
Alors, qu’ont fait les développeurs ? Ils ont remplacé tous ces extraits de code par une version plus simple et plus efficace :
<?php if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly }
Dans ce nouveau code devenu très populaire dans la communauté PHP, l'existence d'une constante a été vérifiée. Cette constante a été définie et affectée d'une valeur dans le premier fichier du flux d'exécution (index.php, home.php ou un fichier similaire). Par conséquent, si cette constante n’existait dans aucun autre fichier de la séquence, cela signifiait que quelqu’un avait contourné index.php et tentait d’accéder directement à un autre fichier.
À ce stade, vous pensez peut-être que briser la chaîne d'exécution doit être extrêmement grave. Cependant, la vérité est que, généralement, cela ne constitue pas une menace majeure.
Le risque peut survenir lorsqu'une erreur PHP expose le chemin d'accès à nos fichiers. Cela ne devrait pas nous concerner si le serveur est configuré pour supprimer les erreurs ; même si les erreurs n’étaient pas cachées, les informations exposées seraient minimes, ne fournissant que quelques indices à un attaquant potentiel.
Il peut également arriver que quelqu'un accède à des fichiers contenant des fragments HTML (vues), révélant une partie de leur contenu. Dans la plupart des cas, cela ne devrait pas non plus être une source de préoccupation.
Enfin, un développeur, soit par erreur, soit par manque d'expérience, peut placer du code risqué sans dépendances externes au milieu d'un flux d'exécution. Ceci est très rare puisque le code du framework ou du CMS dépend généralement d’autres classes, fonctions ou variables externes pour son exécution. Ainsi, si l’on tente d’exécuter un script directement via l’URL, des erreurs surviendront car ces dépendances ne seront pas trouvées et l’exécution ne se poursuivra pas.
Alors, pourquoi ajouter le code constant s'il y a peu de raisons de s'inquiéter ? La réponse est la suivante : "Cette méthode empêche également l'injection accidentelle de variables via une attaque register globals, empêchant le fichier PHP de supposer qu'il se trouve dans l'application alors qu'il ne l'est pas en réalité."
Depuis les débuts de PHP, toutes les variables envoyées via des URL (GET) ou des formulaires (POST) étaient automatiquement converties en variables globales. Par exemple, si le fichier download.php?filepath=/etc/passwd a été accédé, dans le fichier download.php (et dans ceux qui en dépendent dans le flux d'exécution), vous pouvez utiliser echo $filepath; et cela afficherait /etc/passwd.
Dans download.php, il n'y avait aucun moyen de savoir si la variable $filepath avait été créée par un fichier antérieur dans la chaîne d'exécution ou si elle avait été falsifiée via l'URL ou le POST. Cela a créé d’importantes vulnérabilités de sécurité. Regardons un exemple, en supposant que le fichier download.php contient le code suivant :
<?php if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly }
Le développeur avait probablement l'intention d'utiliser un modèle Front Controller pour son code, ce qui signifie que toutes les requêtes Web passeraient par un seul fichier d'entrée (index.php, home.php, etc.). Ce fichier gérerait l'initialisation de la session, chargerait les variables communes et enfin redirigerait la requête vers un script spécifique (dans ce cas, download.php) pour effectuer le téléchargement du fichier.
Cependant, un attaquant pourrait contourner la séquence d'exécution prévue simplement en appelant download.php?filepath=/etc/passwd, comme mentionné précédemment. PHP créerait automatiquement la variable globale $filepath avec la valeur /etc/passwd, permettant à l'attaquant de télécharger ce fichier depuis le système. Grave problème.
Ce n'est que la pointe de l'iceberg puisque des attaques encore plus dangereuses pourraient être exécutées avec un minimum d'effort. Par exemple, dans un code comme celui-ci, que le programmeur aurait pu laisser comme script inachevé :
<?php if (!eregi("modules.php", $HTTP_SERVER_VARS['PHP_SELF'])) { die ("You can't access this file directly..."); }
Un attaquant pourrait exécuter n'importe quel code en utilisant une attaque par inclusion de fichiers à distance (RFI). Si l'attaquant créait un fichier My.class.php sur son propre site https://mysite.net contenant le code qu'il souhaitait exécuter, il pourrait appeler le script vulnérable en passant dans son domaine : useless_code.php?base_path=https : //monsite.net, et l'attaque serait complète.
Autre exemple : dans un script nommé remove_file.inc.php avec le code suivant :
<?php if (!defined('MODULE_FILE')) { die ("You can't access this file directly..."); }
un attaquant pourrait appeler ce fichier directement avec une URL telle que remove_file.inc.php?filename=/etc/hosts, en tentant de supprimer le fichier /etc/hosts du système (si le système le permet, ou d'autres fichiers qu'il avoir la permission de supprimer).
Dans un CMS comme WordPress, qui utilise également des variables globales en interne, ces types d'attaques étaient dévastateurs. Cependant, grâce à une technique constante, ces scripts PHP et d'autres ont été protégés. Regardons le dernier exemple :
<?php if(file_exists($filepath)) { header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="'.basename($filepath).'"'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($filepath)); flush(); // Flush system output buffer readfile($filepath); exit; }
Maintenant, si quelqu'un tentait d'accéder à remove_file.inc.php?filename=/etc/hosts, la constante bloquerait l'accès. Il est essentiel que ce soit une constante car, logiquement, s'il s'agissait d'une variable, un attaquant pourrait l'injecter.
À présent, vous vous demandez peut-être pourquoi PHP a conservé cette fonctionnalité si elle était si dangereuse. De plus, si vous connaissez d’autres langages de script (JSP, Ruby, etc.), vous verrez qu’ils n’ont rien de similaire (c’est pourquoi ils n’utilisent pas non plus la technique des constantes). Rappelons que PHP a été initialement créé comme un système de modèles basé sur C, et ce comportement a facilité le développement. La bonne nouvelle est que, voyant les problèmes que cela causait, les responsables de PHP ont introduit une directive php.ini appelée register_globals (activée par défaut) pour permettre de désactiver cette fonctionnalité.
Mais comme les problèmes persistaient, ils l'ont désactivé par défaut. Malgré cela, de nombreux hébergeurs ont continué à l'activer par crainte que les projets de leurs clients cessent de fonctionner, car une grande partie du code à l'époque n'utilisait pas les variables HTTP_*_VARS recommandées pour accéder aux valeurs GET/POST/... mais utilisait plutôt variables globales.
Finalement, voyant que la situation ne s'améliorait pas, ils ont pris une décision drastique : supprimer cette fonctionnalité dans PHP 5.4 pour éviter tous ces problèmes. Ainsi, aujourd’hui, les scripts comme ceux que nous avons vus (sans utiliser de constantes) ne présentent généralement plus de risque, à l’exception de quelques avertissements/avis inoffensifs dans certains cas.
Aujourd'hui, la technique constante est encore courante. Cependant, la triste réalité – et la raison de cet article – est que peu de développeurs comprennent la véritable raison de son utilisation.
Comme pour d'autres bonnes pratiques du passé (comme copier des paramètres dans des variables locales à l'intérieur de fonctions pour éviter les problèmes de références ou utiliser des traits de soulignement dans des variables privées pour les distinguer), beaucoup continuent de l'appliquer simplement parce que quelqu'un leur a dit un jour que c'était un problème. bonne pratique, sans se demander si elle ajoute encore de la valeur aujourd'hui. La vérité est que, dans la majorité des cas, cette technique n'est plus nécessaire.
Voici quelques raisons pour lesquelles cette pratique a perdu de sa pertinence :
Suppression de *register globals : Depuis PHP 5.4, l'enregistrement automatique des variables GET et POST en tant que globales en PHP a été supprimé. Sans *register globals, l'exécution directe de scripts individuels est inoffensive, supprimant la raison principale de cette technique.
Meilleure conception de code : Même dans les versions antérieures à PHP 5.4, le code moderne est mieux structuré, généralement en classes et fonctions, ce qui rend l'accès ou la manipulation via des variables externes plus difficiles. Même WordPress, qui utilisait traditionnellement des variables globales, minimise ces risques.
Utilisation de *front-controllers : De nos jours, la plupart des applications Web utilisent des *front-controllers bien conçus pour garantir que le code de classe et de fonction ne s'exécute que si l'exécution la chaîne commence au point d’entrée principal. Ainsi, si quelqu'un tente de charger des fichiers de manière isolée, la logique ne se déclenchera que si le flux démarre à partir du bon point d'entrée.
Chargement automatique de classe : Avec l'utilisation généralisée du chargement automatique de classe dans le développement moderne, l'utilisation de include ou require a considérablement diminué. Cela réduit les risques associés à ces méthodes (comme Inclusion de fichiers à distance ou Inclusion de fichiers locaux) parmi les développeurs plus expérimentés.
Séparation du code public et privé : Dans de nombreux CMS et frameworks modernes, le code public (comme les actifs) est séparé du code privé (logique). Cette mesure est particulièrement précieuse, car elle garantit qu'en cas de panne de PHP sur le serveur, le code PHP (qu'il utilise ou non la technique des constantes) n'est pas exposé. Bien que cette séparation n'ait pas été mise en œuvre spécifiquement pour atténuer les registres globaux, elle permet d'éviter d'autres problèmes de sécurité.
Utilisation généralisée d'URL conviviales : De nos jours, la configuration des serveurs pour utiliser des URL conviviales est une pratique courante, garantissant un point d'entrée unique pour la logique des applications. Cela rend presque impossible pour quiconque de charger des fichiers PHP de manière isolée.
Suppression des erreurs en production : la plupart des CMS et frameworks modernes désactivent la sortie d'erreur par défaut, afin que les attaquants ne trouvent pas d'indices sur le fonctionnement interne de l'application, ce qui pourrait faciliter d'autres types d'attaques.
Même si cette technique n’est plus nécessaire dans la majorité des cas, cela ne veut pas dire qu’elle n’est jamais utile. En tant que développeur professionnel, il est essentiel d’analyser chaque situation et de décider si la technique constante est pertinente au contexte spécifique dans lequel vous travaillez. Ce type de pensée critique doit toujours être appliqué, même aux soi-disant meilleures pratiques.
Si vous ne savez toujours pas quand appliquer la technique constante, ces recommandations peuvent vous guider :
Pour tout le reste, si vous avez un doute, appliquez-le. Dans la plupart des cas, cela ne sera pas nocif et pourrait vous protéger dans des circonstances inattendues, surtout si vous débutez. Avec le temps et l'expérience, vous serez en mesure d'évaluer quand appliquer cette technique et d'autres techniques plus efficacement.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!