Remarque : pour suivre cet article, il est supposé que vous avez des connaissances minimales en programmation en PHP.
Cet article concerne un fragment de code PHP que vous avez peut-être vu en haut de votre CMS ou framework préféré et que vous avez probablement lu et que vous devez toujours inclure, par sécurité, dans l'en-tête de tous les fichiers PHP que vous se développer, mais sans explication très claire du 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 en réalité dans presque tous les frameworks et CMS. Dans le cas du CMS Joomla, par exemple, la seule chose qui change est qu'au lieu d'ABSPATH, c'est JEXEC qui est utilisé. Sinon, la logique est la même. Ce CMS est issu d'un autre appelé Mambo, qui utilisait également un code similaire, mais avec _VALID_MOS comme constante. Si l'on remonte encore plus loin dans le temps, on constatera que le premier CMS à utiliser ce type de code était PHP-Nuke (considéré par certains comme le premier CMS en 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 qui, ensemble, répondaient à l'action effectuée par l'utilisateur ou le visiteur sur le Web. Autrement dit, imaginez un site Web de cette époque, sous le domaine example.net et avec ce CMS installé. À chaque chargement de la page d'accueil, le système exécutait une séquence de fichiers de manière ordonnée (dans ce cas, il ne s'agit que d'un exemple, pas d'une séquence réelle) : index.php => load_modules.php => modules.php. Autrement dit, dans cette séquence, index.php a été chargé en premier, puis ce script a chargé load_modules.php, et ceci à son tour 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 ignorer une partie de ce flux en appelant directement l'un des autres fichiers PHP par son URL (par exemple http://example.net/load_modules.php ou http://example.net/modules.php), ce qui , comme nous le verrons, pourrait être dangereux dans de nombreux cas.
Comment ce problème a-t-il été résolu ? Une mesure de sécurité a été introduite, ajoutant des codes au début de chaque fichier, semblable à ceci :
<?php if (!eregi("modules.php", $HTTP_SERVER_VARS['PHP_SELF'])) { die ("You can't access this file directly..."); }
En gros, ce code, situé dans l'en-tête d'un fichier appelé modules.php, vérifiait si modules.php était accessible directement via l'URL. Si tel est le cas, l'exécution a été 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 nous étions dans le flux normal d'exécution et étions autorisés à 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 circonstances, 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 fragments de code par une version plus simple et plus efficace :
<?php if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly }
Dans ce nouveau code, déjà assez courant 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 (index.php ou home.php ou un fichier similaire). Par conséquent, si cette constante n'existait pas dans un autre fichier du flux, cela signifiait que quelqu'un avait ignoré le fichier index.php et essayait d'accéder directement à un autre fichier.
À ce stade, vous pensez sûrement que briser la chaîne d’exécution doit être la chose la plus grave au monde. Cependant, la réalité est que, normalement, cela ne représente pas un danger important.
Un danger peut survenir lorsqu'une erreur PHP expose le chemin d'accès à nos fichiers. Cela ne devrait pas nous inquiéter si la suppression des erreurs est configurée sur le serveur et, même si les erreurs n'étaient pas masquées, les informations exposées seraient minimes, ne fournissant que quelques indices à un éventuel attaquant.
Il peut également arriver que quelqu'un accède à des fichiers contenant des fragments de HTML (à partir de 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, il peut arriver qu'un développeur, soit par inattention, soit par manque d'expérience, insère du code dangereux sans dépendance externe au milieu d'un flux d'exécution. Ceci est très inhabituel, car normalement le code d'un framework ou d'un CMS dépend d'autres classes, fonctions ou variables externes pour son exécution. Par conséquent, si vous essayez d'exécuter un script directement via l'URL, il ne parviendra pas à trouver ces dépendances et ne poursuivra pas son exécution.
Alors pourquoi ajouter le code constant s'il n'y a pratiquement aucune raison de s'inquiéter ? La raison est la suivante : "Cette méthode empêche également l'injection accidentelle de variables via une attaque sur register globals, empêchant le fichier PHP de supposer qu'il se trouve dans l'application alors qu'il ne l'est pas vraiment
."Depuis le début de PHP, toutes les variables envoyées via des URL (GET) ou des formulaires (POST) étaient automatiquement converties en globales. Autrement dit, si le fichier download.php?filepath=/etc/passwd était accédé, dans le fichier download.php (et dans ceux qui en dépendaient dans le flux d'exécution) echo $filepath pourrait être utilisé ; et le résultat serait /etc/passwd.
Dans download.php, il n'y avait aucun moyen de savoir si la variable $filepath avait été créée par un fichier précédent dans le flux d'exécution ou si quelqu'un l'avait usurpée via l'URL ou avec un POST. Cela a généré d’importantes failles de sécurité. Voyons cela avec 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 a probablement pensé à implémenter son code avec un modèle Front Controller, c'est-à-dire faire passer toutes les requêtes Web via un seul fichier d'entrée (index.php, home.php, etc.). Ce fichier serait chargé d'initialiser la session, de charger les variables communes et enfin de rediriger la requête vers un script spécifique (dans ce cas download.php) pour télécharger le fichier.
Cependant, un attaquant pourrait contourner la séquence d'exécution prévue en appelant simplement download.php?filepath=/etc/passwd comme mentionné précédemment. Ainsi, 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 erreur.
Ce n'est que la pointe de l'iceberg, car des attaques encore plus dangereuses pourraient être mené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 à l’aide d’une attaque RFI (Remote File Inclusion). Ainsi, si l'attaquant créait un fichier My.class.php sur son propre site https://mysite.net avec le code qu'il souhaitait exécuter, il pourrait appeler le script vulnérable en lui passant son domaine : codigo_inutil.php?base_path= https:// mysite.net, et l'attaque était terminée.
Autre exemple : dans un script appelé 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 en utilisant une URL du type remove_file.inc.php?filename=/etc/hosts, et ainsi tenter de supprimer le fichier /etc/hosts du système (si le système le permet, ou autre fichiers pour lesquels vous disposez d'autorisations de suppression).
Dans un CMS comme WordPress, qui utilise également des variables globales en interne, ce type d'attaque était dévastateur. Cependant, grâce à une technique constante, ces scripts PHP et d'autres ont été protégés. Voyons cela avec le dernier exemple :
<?php if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly }
Maintenant, si quelqu'un essayait 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 si c'était une variable, bien sûr, l'attaquant pourrait l'injecter.
À ce stade, vous vous demandez probablement 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 pas quelque chose de similaire (c'est pourquoi ils n'utilisent pas non plus la technique des constantes). Rappelons que PHP est né comme un système de templates en C, et que ce comportement a facilité le développement. La bonne nouvelle est que, voyant les problèmes que cela causait, les mainteneurs de PHP ont décidé d'introduire une directive dans le 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 de peur que les projets de leurs clients ne cessent de fonctionner, car une grande partie du code à cette époque n'utilisait pas les variables HTTP_*_VARS recommandées pour accéder aux valeurs GET/POST/..., mais plutôt des variables globales.
Finalement, voyant que la situation ne changeait pas, ils ont pris une décision drastique : supprimer cette fonctionnalité dans PHP 5.4 pour éviter tous ces problèmes. Ainsi, de nos jours, les scripts comme ceux que nous avons vus (sans utiliser de constantes) ne normalement ne présentent plus de danger, à l'exception de quelques avertissements/notifications inoffensifs dans certains cas.
Aujourd'hui, la technique constante est encore courante. Cependant, ce qui est triste – et la raison qui a conduit à ce post – c'est que peu de développeurs connaissent la véritable raison de son utilisation.
Comme d'autres bonnes pratiques du passé (telles que copier les paramètres d'une fonction vers des variables locales pour éviter les dangers liés aux références dans l'appel, ou utiliser des traits de soulignement dans des variables privées pour les distinguer), beaucoup l'appliquent encore simplement parce que quelqu'un une fois leur a dit que c'était une bonne pratique, sans se demander si cela ajoutait réellement de la valeur à l'époque actuelle. La réalité est que, dans la majorité des cas, cette technique n'est plus nécessaire.
Certaines raisons pour lesquelles cette pratique a perdu de sa pertinence sont les suivantes :
Disparition des *register globals : Depuis PHP 5.4, la fonctionnalité d'enregistrement des variables GET et POST en tant que variables globales PHP n'existe plus. Comme nous l'avons vu, sans *register globals, l'exécution de scripts individuels devient inoffensive, éliminant la raison principale de cette pratique.
Meilleure conception dans le code actuel : Même dans les versions antérieures à PHP 5.4, le code moderne est mieux conçu, structuré en classes et fonctions, ce qui complique l'accès ou la manipulation via des variables externes. Même WordPress, qui utilise souvent 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, qui garantissent que le code des classes et des fonctions sera ne sera exécuté que si la chaîne d'exécution commence au point d'entrée principal. Peu importe donc si quelqu'un essaie de télécharger des fichiers de manière isolée : la logique ne s'activera pas si le flux n'est pas démarré au bon point.
Chargement automatique des classes : Étant donné que le chargement automatique des classes est utilisé dans le développement actuel, l'utilisation de include ou require a été considérablement réduite. Cela signifie que, sauf si vous êtes un développeur novice, il ne devrait y avoir aucun includes ou requires qui pourraient présenter des risques (tels que Inclusion de fichiers à distance ou Inclusion de fichiers locaux).
Séparation du code public et privé : Dans de nombreux CMS et frameworks modernes, le code public (tel que les actifs) est séparé du code privé (code de programmation). Cette mesure est particulièrement précieuse car elle garantit qu'en cas de panne de PHP sur le serveur, le code des fichiers PHP (qu'ils utilisent ou non la technique constante) n'est pas exposé. Bien que cela n'ait pas été spécifiquement mis en œuvre pour atténuer les registres globaux, cela permet d'éviter d'autres problèmes de sécurité.
Utilisation étendue d'URL conviviales : De nos jours, il est courant de configurer le serveur pour utiliser des URL conviviales, ce qui force toujours un point d'entrée unique à la programmation. Cela rend presque impossible pour quiconque de télécharger des fichiers PHP de manière isolée.
Suppression de la sortie d'erreur en production : La plupart des CMS et frameworks modernes bloquent la sortie d'erreur par défaut, de sorte que les attaquants ne peuvent pas trouver d'indices sur le fonctionnement interne de l'application, ce qui pourrait faciliter d'autres types d'attaques.
Bien que cette technique ne soit 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 cas et de décider si la technique constante est pertinente dans le contexte précis dans lequel vous travaillez. C'est un critère que vous devez toujours appliquer, même dans ce que vous considérez comme de bonnes pratiques.
Si vous ne savez toujours pas quand appliquer la technique constante, ces recommandations peuvent vous guider :
Pour tout le reste, si vous avez des doutes, appliquez-le. Dans la plupart des cas, cela ne devrait pas être 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 de mieux évaluer quand appliquer cette technique et d'autres techniques.
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!