Checklist sécurité pour les applications web en PHP

En quelques mots

Petite check-list contenant les vulnérabilités les plus communes des applications web et les solutions et bonnes pratiques applicables en PHP. Bien qu’axée sur le PHP cette checklist peut être étendue à tous les langages de programmation.

Validation des entrées

Conseils:

En général il est conseillé d’utiliser le concept de liste blanche pour de nombreux cas de validation.

Pour avoir une couche de protection supplémentaire ou pour protéger une application déjà existante pensez aux ajouts « PHP Input Filter » et « PHPIDS » ou « mod_security » si vous utilisez Apache

Injection SQL

Utiliser des requêtes SQL brutes en concaténant les variables à la chaîne de la requête est une mauvaise pratique qui peut facilement mener à l’injection de SQL non désiré.

Conseils :

Cross-site scripting (XSS)

Consiste en l’introduction de javascript en entrée qui sera alors exécuté lors de l’affichage. Il s’agit d’une vulnérabilité très commune et dangereuse, bien qu’elle soit facile à éviter dans beaucoup de cas. Vous trouverez une liste assez complète des attaques possibles sur le site de l’OWASP.

En général, il faut écrire une fonction générique d’affichage, qui devra être impérativement utilisée lors de tout affichage.

Injection de code

Injection de code malicieux qui sera exécuté par l’application.

Injection d´instructions

Certaines fonctions permettent d’exécuter des instructions système.

Sécurité de la session

En volant l’identifiant de la session, il est possible d’utiliser la session d’autres utilisateurs. Voir « firesheep » par exemple.

Cross-Site Request Forgery (XSRF)

Consiste en l’utilisation de la session ouverte dans un autre tab du navigateur par une page web malicieuse. Le plus souvent les parties vulnérables seront les formulaires, qui pourraient être remplis par un site malveillant. Dans ce cas il est facile d’éviter ce genre d’attaque en créant un identifiant unique pour le formulaire et contrôlant cet identifiant lors de la soumission. Une bonne pratique consiste à utiliser un nonce crée par une fonction du type uniqid ou pseudoaléatoire haché avec un code secret en utilisant la fonction hash_hmac, que l’on met dans un champ hidden du formulaire ainsi qu’en session, et qui sera vérifié lors de la soumission.

Pour protéger les fonctionnalités en dehors des formulaires, il faudra faire une analyse au cas par cas.

Bonnes pratiques pour le stockage des mots de passe utilisateurs dans la base de données

Les mots de passe doivent être stockés de manière hachée dans la base de données. Pour cela il ne faut surtout par utiliser des algorithmes du type md5 ou sha1 pour lesquels il existe des « rainbow tables », qui permettent l’obtention rapide du mot de passe à partir du hash.

De plus, pour justement éviter l’utilisation de « rainbow tables », et pour masquer les hashs qui seraient identiques car issus du même mot de passe, il est important d’utiliser un « salt ». Ce « sel » sera déterminé de manière aléatoire et concaténé au mot de passe avant hachage. On stockera ensuite le sel ainsi que le hash dans la base de données.

Il est important aussi de rajouter un « salt » constant secret qui serait stocké ailleurs que dans la base, par exemple dans un fichier de configuration.

L’utilisation d’un salt concaténé peut avantageusement être remplacée par une fonction de hachage hash_hmac, mais nous recommandons l’utilisation de la fonction crypt (https://php.net/manual/en/function.crypt.php) qui permet d’ajouter un facteur de coût au calcul quand on utilise CRYPT_BLOWFISH, CRYPT_SHA256 ou CRYPT_SHA512 (voir key stretching).

Au final le calcul de hash aura par exemple la forme suivante :

$hash=crypt(hash_hmac('sha512','secret password',$saltsecretconfig), '$2y$’.$cost.'$randomsaltDB$') ;

La variable $cout devra être ajustée à un nombre tel que la durée de l’opération soit viable pour votre application mais prohibitive pour une attaque brute-force (1/100 secondes par exemple).

PHP 5.5 introduit des nouvelles fonctions de hashage qui permettront de stocker de manière sûre les mots de passe (voir https://www.php.net/manual/en/function.password-hash.php).

La fonction ci-dessus devient alors:

$hash = password_hash(hash_hmac(sha512, secret password, $saltsecretconfig), PASSWORD_BCRYPT, ['cost'=>$cost]); // from PHP 5.5

Utilisation de contremesures

En général une application web sera scannée à l’aide d’un outil automatique par un attaquant avant compromission. De ce fait, il est possible de parsemer son application de pièges à ours où autres « tar pits ». Un exemple pratique appelé « weblabyrinth » peut être téléchargé surhttp://www.mayhemiclabs.com/content/new-tool-weblabyrinth. Mais des scripts inutiles contenants des fonctions « sleep » ou autres fausses authentifications peuvent déjà faire l’affaire.

Webroot != Approot

Avoir ses bibliothèques et autres scripts dans des endroits inaccessibles directement depuis Internet est d’un avantage certain. En cas de vulnérabilités de certaines bibliothèques elles ne peuvent pas être exploitées directement.

Déni de service

Il est impossible de se défendre de manière absolue contre les attaques de type « déni de service ». Il est néanmoins possible d’essayer d’optimiser son application pour qu’elle ait un meilleur répondant. Bien que cela dépasse le cadre de ce document voici quelques concepts que vous pourrez approfondir :

Frameworks

Beaucoup de frameworks sont écrits par des développeurs chevronnés et profitent de leur expérience étendue, non seulement en termes de sécurité, mais aussi de bonnes pratiques de programmation. Ils incluent donc souvent beaucoup des techniques exposées ci-dessus et permettent le développement rapide et plus sûr d’applications web. Exemples : Zend, Symfony,…

Conseils pour php.ini

La fonction de configuration open_basedir permet de définir les répertoires auxquels l’application a accès et apporte donc une sécurité accrue.

Les paramètres suivants dans php.ini doivent être ajustés aux besoins de votre application tout en essayant de les garder le plus bas possible. En général il est avantageux de régler ces paramètres à la volée avec ini_set dans les scripts qui auraient besoin de plus de ressources que le gros de l’application :

Vérifier cette valeur en environnement de production :