Mis à jour 1. Déc 2020

PHP reste le langage de programmation côté serveur le plus populaire : il est utilisé par presque 80% des sites (source). Ce langage continue à être développée, et PHP 8 a été publié la semaine dernière ! Cette version apporte de nouvelles fonctionnalités et devrait renforcer la sécurité. 

Cependant, la sécurité de PHP se construit dès ses fonctionnalités centrales ‘historiques’. L’article qui suit ne remplace pas de bonnes connaissances en PHP, et il n’y aura rien à copier/coller directement dans vos fichiers. Mais nous croyons que ces conseils et bonnes pratiques vous apporteront des bénéfices à long terme, en comprenant et en appliquant les différents points en fonction de vos besoins et de votre contexte.

L’article couvre la configuration de PHP, les mises à jour, l’organisation du code et le filtrage/échappement des données.

Configuration et mises à jour

Tout commence par une bonne connaissance de ce que votre serveur web contient. Posez-vous ces quelques questions :

  • Quelle version de PHP est installée sur mon serveur ?
  • Quelles librairies sont utilisées ?
  • Quel code est inclus depuis l’extérieur? Puis-je lui accorder ma confiance ?
  • Quel framework est utilisé, et dans quelle version ?
  • Le système d’exploitation est-il à jour ?

Si vous ne pouvez pas répondre de manière sûre à ces questions, alors il vous manque des informations. Avoir une liste centralisée et à jour des composants de votre architecture web est clé pour la sécurité de votre site.

A partir de cette liste, vous devez ensuite pouvoir répondre à deux questions :

  • Ai-je vraiment besoin de ce composant ? Si non, alors il doit être supprimé. Un composant inutilisé est un risque inutile pour votre sécurité.
  • Comment sais-je si une mise à jour est disponible pour ce composant ? (mailing-list, twitter, flux RSS…)

La configuration de la sécurité est un processus continu qui doit être repris de manière régulière. Cette règle semble évidente au départ d’un projet, mais est assez rarement appliquée plusieurs mois après que le site web est en production.

Keep it simple


Avant d’aller plus en avant dans les différentes pratiques de sécurité, vous devez vous rappeler un proverbe critique : less is more (« moins donne plus »).
Sécurité complexe = mauvais sécurité. Il faut conserver de la simplicité, et de l’efficacité. Rappelez-vous aussi que « 100% sécurisé » n’existe pas.

Configurer PHP lui-même est une étape clé. Avoir un environnement à jour n’est pas suffisant.

Rapports d’erreurs

Les notifications d’erreurs sont très pratiques pour comprendre ce qui ne va pas dans le code d’une application, mais il ne faut laisser ces informations visibles que dans les environnements de développement/staging !
Votre fichier php.ini vous permet de configurer trois éléments importants :

  • error_reporting
  • display_errors
  • log_errors

Sur un environnement de développement/staging, vous pouvez aussi bien afficher que logger les erreurs, mais ne pas les afficher sur un environnement de production. Afficher les erreurs sur la production n’est pas très joli, mais surtout les attaquants en sont friands (elles donnent de précieuses informations sur votre architecture technique).

Logger les erreurs peut être intéressant en production, mais il faut alors bien choisir quel type d’erreur on souhaite enregistrer, notamment sur les sites à fort trafic (les logs peuvent donner lieu à des fichiers très volumineux).
C’est là qu’intervient le paramètre “error_reporting”, vous permettant de spécifier quels types d’erreurs vous souhaiter garder. Les valeurs peuvent être E_ALL (ou E_ALL | E_STRCT en fonction de votre version de PHP), ou par exemple E_ALL ^(E_STRICT | E_DEPRECATED | E_NOTICE).
En savoir plus sur ce paramètre : http://php.net/manual/fr/errorfunc.configuration.php#ini.error-reporting

D’autres éléments du php.ini impactant la sécurité nécessitent d’être revus. Vous pouvez parcourir la liste ci-dessous et revoir les spécifications sur la page dédiée aux directives php.ini.

disable_functions
enable_dl
memory_limit
max_execution_time
post_max_size
max_input_time
file_uploads
max_file_uploads
upload_max_filesize
upload_tmp_dir
allow_url_fopen
allow_url_include
expose_php

Organiser votre code proprement

Une bonne pratique consiste à garder code privé, classes et librairies tierces en dehors de la racine du site (web root). Si pour quelque raison un attaquant peut récupérer des fichiers bruts depuis la racine de votre site, votre code restera un peu plus sécurisé (et empêchera ainsi du reverse-engineering).
Au contraire, si vos classes et librairies sont stockées dans la racine du serveur, par exemple www/my-classes/libX1.031/, alors les attaquants pourront détecter quelles librairies vous utilisez et sur quelle version.

“Filtrer en entrée, échapper en sortie »

Un principe en sécurité est de ne jamais faire confiance aux utilisateurs. Vos utilisateurs légitimes ne vont probablement pas essayer de pirater votre site, mais les attaquants le font. Etant donné qu’il est difficile de faire la différence entre utilisateurs légitimes et malicieux, il faut être prudent avec ce que vous recevez de leur part.

Filtrage de données - illustration

Filtrer les entrées

Filtrer les entrées vous permet d’accepter ou de rejeter les données fournies par les utilisateurs. Ces données peuvent être issues de paramètres d’URL (GET), de formulaires (POST), de cookies, ou encore de headers HTTP.
Une bonne stratégie en termes de validation de données est de définir ce que vous attendez de vos utilisateurs. Cela peut être un format, un ensemble de valeurs possibles. Maintenir une liste à jour des points d’entrée vous permettra de garder une trace de tous les éléments que vous devez filtrer.
D’un point de vue technique, le filtrage peut être réalisé à l’aide d’expressions régulières (si vous excellez dans l’écriture des règles) ou en utilisant des librairies externes de filtrage comme “Respect/Validation”.
https://github.com/Respect/Validation (régulièrement maintenue, il y a quelques jours au moment de l’écriture de cet article).

Echapper les données

Filtrer les données n’est pas assez!
Chaque information soumise par les utilisateurs, aussi petite soit-elle, doit être échappée. Cela s’applique, que les données soient tout d’abord stockées dans une base de données, ou directement renvoyées en sortie (html, javascript, json…)
Les fonctions natives de PHP permettent d’effectuer ce type d’actions :

htmlspecialchars() – http://php.net/manual/fr/function.htmlspecialchars.php
htmlentities() – http://php.net/manual/fr/function.htmlentities.php
strip_tags() – http://php.net/manual/fr/function.strip-tags.php
urlencode() – http://php.net/manual/fr/function.urlencode.php
json_encode() – http://php.net/manual/fr/function.json-encode.php
mysqli_real_escape_string() – http://php.net/manual/fr/mysqli.real-escape-string.php
addslashes() – http://php.net/manual/fr/function.addslashes.php

Une autre option est utiliser la fonction filter_var, avec un paramètre de filtre de nettoyage :
http://php.net/manual/fr/function.filter-var.php
http://php.net/manual/fr/filter.filters.sanitize.php

Le prochain article de cette série, “Bonnes pratiques de sécurité pour PHP partie 2” couvre la protection contre les attaques les plus courantes (injections, XSS, session hijacking, parameter tampering).