Sécurité PHP

Les informations de cet article datent

L’actualité étant chargée niveau hack, il est temps de sortir un petit article présentant différentes méthodes de sécurisation d’une application PHP.
Je vais donc brièvement vous présenter différentes librairies ou classes PHP orientées sécurité.

  • PHPIDS
    Un monitoring complet des actions de l’utilisateur avec évaluation de la dangerosité de chaque requête, et de la session. L’outil indispensable pour détecter les recherches de failles.
  • Input filter
    Cette librairie permet de filtrer les tableaux $_GET, $_POST, $_REQUEST pour y detecter le PHP, JavaScript et HTML.
  • Anti csrf
    Permet d’ajouter un token dans ses formulaires afin de ne pas se les faire détourner. Le but étant que chaque formulaire ne soit soumis qu’une seule fois.

L’utilisation des librairies précédentes est un complément mais elles ne constituent pas une sécurité optimale à elles seules.

Voici donc une liste de bonnes pratiques niveau sécurité :

  • Ne jamais insérer des données dans une requête SQL sans un minimum de traitement. ( mysql_real_escape_string ou mieux PDO::prepare() et PDOStatment::bindValue() )
  • Ne jamais faire passer en paramètre une page à inclure
    exemple : include $_GET[“ma_page”]; préférez une solution du type : switch($_GET[“ma_page”]){ case “home” : include “home.php”; break; case “forum” : include “forum.php”; break; default : inclue “error.php” break; }
  • Avoir un fichier index.html dans chaque dossier du projet, contenant <html> <head></head> <body> <h1>Accés interdit</h1> </body> </html>
  • Sécuriser ses session_start contre le vol de session. $ip = !empty( $_SERVER[‘HTTP_X_FORWARDED_FOR’] ) ? $_SERVER[‘HTTP_X_FORWARDED_FOR’] : $_SERVER[‘REMOTE_ADDR’]; $securite = $ip.’_’.$_SERVER[‘HTTP_USER_AGENT’]; if(empty($_SESSION)) { session_start(); $_SESSION[‘securite’] = $securite; } elseif($_SESSION[‘securite’] != $securite) { session_regenerate_id(); $_SESSION = array(); } Voici donc une technique qui permet simplement de tester si une session est bien utilisée par le même navigateur sur la même machine.
  • Crypter les données sensibles de votre base (email,pseudo par exemple ).
    J’ai mis au point cette classe : Crypage.   Elle n’est certes pas extrêmement puissante mais elle permet de se protéger contre une injection SQL. Pensez bien sûr à changer la clef 😛 .
  • Le cryptage des mots de passe est une base de la sécurité. Pour cela utilisez une fonction comme celle-ci : protected function crypteMotPasse($mot){ $salt = “!123 GRAIN DE SEL#”; return sha1(strrev(ucfirst($mot).$salt)); } Ceci est une idée le but est d’éviter que les mots de passe soient facilement lisibles en cas de piratage de notre base de données. Tout en sachant que des rainbow table existent pour inverser un md5 ou un sha1.
    Vous pouvez aussi définir un salt par user. Pour cela, au moment de la création du compte, vous générez une chaîne de caractères le plus aléatoirement possible, que vous stockez dans votre table user. Pour la connexion de l’utilisateur, il faudra dans un premier temps aller chercher le salt dans la base. Puis utilisez la fonction ci-dessus comme suit : protected function crypteMotPasse($mot,$saltuser){ $salt = “!123 SALT FIXE#”.$saltuser; return sha1(strrev(ucfirst($mot).$salt)); } Comme le rappelle Simon dans son commentaire : En effet, l’attaquant, pour obtenir l’ensemble des données de la base doit :
    – avec la fonction crypteMotPasse (salt à l’intérieur de la fonction) : regénérer une rainbow table
    – avec une fonction qui prend un salt différent par utilisateur : regénérer autant de rainbow table que d’utilisateurs
  • Empêcher un accès direct à vos fichiers PHP. Dans votre/vos fichier(s) d’entrée(s) (index.php par exemple) faites un : define(“_BASE_URL”,TRUE); Puis commencez tous vos autres fichiers PHP par : <?php if(!defined(‘_BASE_URL’)) die(“Erreur, la page est indisponible”);
  • Ajoutez la ligne suivante dans votre/vos fichier(s) d’entrée(s) afin d’activer les logs d’erreur PHP. Ceci est très utile pour débugger votre application lors de la phase de développement. Il faut cepandant penser à enlever cette ligne au moment de la mise en production. <?php error_reporting(E_ALL); ?>
  • Ne stockez que le minimum vital dans les cookies (voire rien si possible), réduisez la durée de vie de vos cookies, et cryptez les données qui sont dedans.
  • Pour les utilisateurs de CodeIgniter, activez le stockage de la session en base de données.
    Documentation allez à “Saving Session Data to a Database”

6 commentaires sur “Sécurité PHP”

  1. Article très utile, c’est vrai que bon nombre de développeurs (surtout débutants) ne prennent pas en compte cet aspect primordial qu’est la sécurité

  2. Bonjour,
    concernant le error_reporting(E_ALL), il est a utiliser pour la phase de développement mais à mettre à 0 en production pour viter de renvoyer des erreurs donnant des indications exploitables à un éventuel pirate.

  3. Dans l’ideé, l’article comporte de bons conseils, néanmoins il y a quelques erreurs :
    1. seul mysql_real_escape_string est rellement efficace, l’utilisation de addslashes et magic quotes n’est pas efficace contre certaines attaques. Dans l’idéal, il faudrait au maximum utiliser PDO avec surtout l’utilisation de PDO::prepare() et PDOStatment::bind*()
    L’utilisation de PDO permet de contrer des erreurs btes comme un SELECT * FROM table WHERE id = $id -> même si pour $id on met un mysql_real_escape_string, avec l’absence de quote on pourra faire une injection SQL, une bonne utilisation de PDO contrera ce genre d’erreur
    2. Le vol de session se base sur le vol de l’identifiant de session pass par un cookie ou dans l’URL, le fait de vérifier un $_SESSION[‘valide’] ne changera rien, car si l’identifiant est vol, le voleur aura bien le $_SESSION[‘valide’]. Pour contrer les vols de sessions, il faudra aller plus loin (rgnrer les identifiants chaque page et vrifier si un ancien identifiant n’est pas appel par exemple)
    3. l’idéal serait d’avoir un salt par utilisateur cela complique normment les reverse de donnes. En effet, l’attaquant pour obtenir l’ensemble des donnes de la base doit :
    – avec la fonction crypteMotPasse (salt l’intrieur de la fonction) : regnrer une rainbow table
    – avec une fonction qui prend un salt diffrent par utilisateur : regnrer autant de rainbow table que d’utilisateur
    4. le if(!isset(_BASE_URL)) ne fonctionne pas : isset permet de tester des variables, il faut utiliser if (!defined(‘_BASE_URL’)) pour tester les contantes, mais à part la mauvaise fonction, c’est une bonne technique.
    Enfin une question, en quoi l’utilisation du stockage de session en base de donnes plutôt que dans des fichiers est-il plus sécurisé ?

  4. Le stockage des session en base de donnes permet une diminution de la limite de vie de vos sessions fixée par votre hébergeur. Par conséquent il diminue les chance de se faire coler sa session.
    Merci pour les petites corrections apportées à mon article. Je viens de le modifier.

Les commentaires sont fermés.