Archive pour le ‘PHP’ catégorie

open_basedir sur Plesk 10.3

5 avril 2012

Le système d’interface Plesk utilise pour ses hébergements par défaut un système apellé « open_basedir ». C’est une restriction entre PHP et Apache qui interdit à PHP d’aller chercher par des includes ou fopen des fichiers ailleurs que dans le dossier racine (et sous-dossiers) d’un site. Par exemple, ceci va interdire à un site d’aller chercher des composants dans un dossier racine d’un sous-domaine, ou au contraire, d’interdire à un sous-domaine d’aller chercher des modules sur le domaine principale.

Ce système open_basedir peut être assez facilement modifié, mais il faut, sur Plesk 10.3 et les versions précédentes, aller faire les modifications en mode terminal : L’interface Plesk ne permet pas de faire ces modifs.

Tout d’abord, il faut savoir quels dossiers contiennent les informations qui vous intéressent. Dans l’exemple suivant, je vais utiliser l’arborescence suivant :

Répertoire principal du site mondomaine.com

   /var/www/vhosts/mondomaine.com/httpdocs/

Répertoire principal du sous-domaine sousdomaine.mondomaine.com


   /var/www/vhosts/mondomaine.com/monsousdomaine/

Imaginons que le site http://monsousdomaine.mondomaine.com doit aller chercher un fichier parametres.php qui se trouve, pour une gestion centralisée de votre site, dans la racine, sous le dossier /httpdocs :

Avec une restriction open_basedir, votre commande PHP d’include va échouer car le le dossier httpdocs n’est pas un sous-dossier de monsousdomaine. Il faut passer outre le paramétrage par défaut, en modifiant (ou en créant si inexistant) un fichier vhost.conf dans un dossier spécifique.

Si votre sous-domaine monsousdomine doit accéder à un fichier dans la racine du site principale et vous souhaitez lui retirer toute sécurité open_basedir, vous deviez ajouter le code suivant dans le fichier /var/www/vhosts/mondomaine.com/subdomains/monsousdomaine/conf/vhosts.conf


   <Directory /var/www/vhosts/mondomaine.com/subdomains/monsousdomaine>
      php_admin_value open_basedir none
   </Directory>

Au contraire, si vous devez modifier votre domaine princpal pour désactiver open_basedir, vous devez modifier le fichier suivant : /var/www/mondomaine.com/conf.vhost.conf et ajouter le code suivant :


   <Directory /var/www/vhosts/mondomaine.com/httpdocs>
      php_admin_value open_basedir none
   </Directory>

Le fait de retirer toute protection open_basedir est souvent discuté car cela peut ajouter une faille de sécurité, en laissant un script libre cours et libre accès à toute l’arboresence du serveur à laquelle le compte utilisateur Apache a accès, pour lire et déposer des fichiers.

Il peut être mieux simplement d’ajouter explicitement les dossiers que vous souhaitez autoriser.

Pour ce faire, au lieu de modifier les fichiers conf pour spécifier « open_basedir none », vous aller spécifier à la place de none, tous les chemins autorisés, séparés par des deux-points :


   php_admin_value open_basedir /var/www/vhosts/mondomaine.com/httpdocs:/var/www/vhosts/mondmaine.com/monsousdomaine:/tmp

Ceci donnera accès aux dossiers racine du domaine, du sous-domaine et au dossier temporaire du serveur. Bien évidamment, ceci ne focntionnera que pour le site ou sous domaine qui correspond au fichier conf modifié.

Une fois vos création ou modification des fichiers vhost.conf effectués, vous devez relancer les commandes suivantes pour que plesk regénère ses propres fichiers de paramètres, et pour être certain qu’apache a été rechargé :


   /usr/local/psa/admin/bin/httpdmng --reconfigure-all
   /etc/init.d/httpd reload

Assume Nothing

4 avril 2012

Au milieu des années 90, j’ai acheté un livre de Michael Abrash, Programmation 486 et Pentium, qui présentait l’architecture de ces 2 processeurs, et tirait beaucoup de matière d’un autre de ses livres, « Zen of code optimisation », qui discutait de l’implémentation du code, et comment optimiser une programme, comment une programme pourrait être accéléré d’un facteur de 100 en repensant certaines parties, en « pensant autrement ».

En revanche, je crois que dans mon expérience, la seule règle qui revient très souvent c’est « Assume Nothing » : Ne prenez rien pour argent comptant, et si ce n’est pas une fois par jour que cette règle se vérifie, c’est une fois par semaine. Depuis 15 ans.

Quand vous codez quelque chose, vérifiez. Contrôlez vos erreurs. Contrôlez les données qui passent par votre programme. Ne prenez aucune donnée comme argent comptant tant qu’il n’ a pas été clairement identifié. Le contrôle d’erreur ralentit certes une programme, mais il vaut largement mieux avoir un erreur géré soit par un message, soit par un traitement « par défaut » que de foncer tête baissé, et se ramasser quand quelque chose va de travers.

Dernière exemple en date : J’affiche sur une page web des vins avec un classement d’étoiles : 0, 1, 2 ou 3. Cette valeur d’étoiles est stockée dans notre base de données, par vin, et il y a 4 logos générés, correspondant à chacune de ses valeurs.

Le code qui gère l’affichage de la page va chercher l’image du logo correspondant en calculant son nom, à partir de la valeur en base + « .jpg » :

   monImage = maValeur+".jpg"

Dans l’absolu, rien d’étonnant et ça va marcher… sauf quand la valeur dans « maValeur » ne correspond à rien de prévue : si le nom d’image reconstitué ne correspond pas à une image sur le site, une croix rouge s’affichera sur la page. Et oui, dans un vin, j’ai « -1″ étoiles. Sur un autre, j’ai tout simplement « Aucune ». Le développeur a prévue « 0.jpg », « 1.jpg », « 2.jpg » et « 3.jpg ». Et pour mon Aucune, ou mon -1 ? Est ce qu’il existe un « -1.jpg » ou « Aucune.jpg » – ou tout simplement « .jpg » si quelqu’un réussi à saisir une valeur vide… Eh bien non… Et s’il y a 2 valeurs d’étoile différentes pour le même vin ? Non, pas géré non plus.

Comment faire ? en vérifiant les valeurs. Ce n’est pas parce qu’on ne devrait trouver 1 valeur correspondant à 0, 1, 2 ou 3 qu’on va trouver forcément toujours l’une de ces valeurs, donc vous validez explicitement ce qui devrait s’y trouver, et vous devez gérer un cas de plus : Le cas qu’il y a un erreur et qu’il n’y a pas de correspondance. Vous gérez l’erreur :

   SI maValeur = 0, 1 , 2 ou 3 ALORS
      monImage = maValeur+".jpg"
   AUTREMENT
      monImage = "inconnu.jpg"
   FIN SI

Oui, ça implique de gérer un cas d’erreur avec un image nommé « inconnu.jpg », mais le travail est fait correctement et vous n’affichez pas des données ou liens fantaisistes, et vous gardez le contrôle sur votre application.

Est ce que c’est vraiment grave ? Ca dépends de son métier et de l’application. Dans mon exemple tiré d’un bout de code que je viens de rencontrer c’est dommage sur un site d’afficher une croix rouge sur une image car un développeur a voulu s’économiser 20 secondes, mais il n’y a pas mort d’homme. Parfois c’est plus grave , en temps, en argent et parfois même en vies.

Deux exemples :

  • Un calculateur pouvait gérer un chiffre entre -32767 et +32767 pour gérer l’équilibre d’une machine. Que s’est il passé quand la valeur est passé à +32768 ? (max+1 ?) Et bien, le chiffre s’est inversé en négatif, tout simplement. Et l’équilibre en a fait autant. C’était installé où ce fameux calculateur ? A bord le premier tir d’Ariane 5 qui a été détruit quelques secondes après le lancement pour cause de perte de cap et d’équilibre.
  • Un appareil de radiothérapie devait se couper quand le cache de visée était en mouvement, et l’ordinateur définissait un mouvement en calculant le déplacement du moteur du cache, mais ce mouvement n’était calculé si le moteur de ce cache tournait dans un sens. S’il tournait dans l’autre sens, le rayonnement n’était pas coupé, et des personnes ont subies des irradiations, mortelles dans certains cas.

Que le code soit 3 lignes qui pilotent l’affichage des images sur un site web, ou 50 000 lignes qui pilotent une fusée, il suffit d’une simple non vérification pour que tout bascule, et le plupart de temps, passer 30 secondes de plus sur un sujet permet de neutraliser le problème.

Pour revenir à mon code, comment est-ce que j’aurais géré ce cas, en connaissant tout aussi bien les données que la personne ayant écrit le code à l’origine, et que les données sont potentiellement mauvaises ? :


   //Commençons par une valeur qui prévoit l'erreur
   monImage = "inconnu.jpg"
 
   SI maValeur est un tableau
      //Prendre le denrier élément du tableau
      maNouvelleValeur = maValeur[compter_elements(maValeur)-1]
      envoyer_email(admin, "Plusieurs étoiles pour un même vin")
      //Ecraser le tableau avec la nouvelle valeur détectée après avoir signalé l'incohérence
      maValeur = maNouvelleValeur
   FIN SI
 
   //Tenter de convertir en chiffre
   maValeur = convertir_numerique(maValeur)
   SI maValeur n'est pas explicitement en erreur
      SI maValeur = 0, 1 , 2 ou 3 ALORS
         Je suis bon. Ecraser la valeur dans monImage par la valeur que je viens de calculer
         monImage = maValeur+".jpg"
      AUTREMENT
         //J'ai trouvé un chiffre, mais pas celui prévu
         envoyer_email(admin, "Valeur erronée dans la quantité d'étoiles")
      FIN SI
   AUTREMENT
      //La valeur que j'ai trouvé n'était pas un chiffre
      envoyer_email(admin, "erreur de donnée dans les étoiles")
   FIN SI

Je part du principe que la donnée est erronée en prévoyant au départ une valeur par défaut qui sera explicitement validée si je trouve une valeur correcte.

Je vérifie qu’il n’y a pas de tableau de valeurs (plusieurs valeurs pour un même vin), et s’il y en a, on prends la dernière valeur connue pour en avoir au moins une : Je tente de corriger. On sait qu’il y a déjà une erreur et on le signale, mais nous pouvons peut être quand même pouvoir continuer avec une donnée qui serait peut être valide.

Je vérifie ensuite si la donnée est un chiffre, en le convertissant. S’il y a une erreur, nous remontons une alerte sans aller plus loin, autrement on continue.

Ensuite, si je continue, je vérifie que la donnée est dans la plage connue et prévue : si oui, je valide la donnée et je construit le nom de l’image, autrement, je remonte une alerte, et je garde l’image prévue au début.

Si rien ne passe, j’ai une image prévue par défaut (inconnu.jpg), si la donnée est correcte, le nom d’image sera modifiée par le nom correcte, et dans tous les autres cas de figures, je sais exactement ce qui s’est passé, pourquoi la donnée ne va pas, et l’erreur m’est remontée par email pour être corrigé. Et l’internaute verra toujours une image sur le site, et non pas une croix rouge pour signaler une image cassée.

En résumé, si vous traitez des valeurs, vérifiez. Toujours. Assume nothing!