Retour à la liste

Audit PHP : sécurité et bonnes pratiques

Créé : 21.07.2015, 15:52:03  -  Modifié : 17.05.2018, 21:56:06

Créé en 1994, PHP (Hypertext Preprocessor) est un langage de programmation très répandu sur Internet.

C’est un langage puissant, facile à aborder et permissif.
De ce fait, il est fréquent de retrouver des codes PHP qui ne respectent pas les normes élémentaires de sécurité et de bonnes pratiques.

Cet article est un aide-mémoire qui dresse une liste (non exhaustive) des points à contrôler dans le cadre d’une revue de code.

 

Bonnes pratiques

ID Description
BEST-PRACTICES-LASTEST-VERSION Être à jour : récupérer les dernières versions de PHP et des bibliothèques.
BEST-PRACTICES-PHP-TAG Utiliser les tags <?php et ?> pour écrire du code PHP car ils assurent une portabilité totale sur tous les serveurs et pour toutes les versions de PHP. De plus, ils évitent les conflits avec d’autres langages comme le XML. (cf.Désactivation de short_open_tag)
BEST-PRACTICES-CHARSET Spécifier l’encodage dans la balise meta des pages HTML (ex: <meta charset= »utf-8″ /> )
BEST-PRACTICES-DOCUMENT-TYPE Spécifier le type de document dans chaque page HTML (ex: <!doctype html> )
BEST-PRACTICES-QUOTE-CHARACTERS Mettre les chaînes de caractères entre simple quotes ‘…’ : le traitement est plus rapide qu’avec les doubles quotes « … » car l’interpréteur PHP doit vérifier s’il y a des variables entre les doubles quotes.
BEST-PRACTICES-QUOTE-ARRAYS Utiliser des quotes (simples, de préférence) pour accéder aux valeurs d’un tableau sinon PHP pense qu’il s’agit d’une constante.Exemple : $row['id'] est 7 fois plus rapide que $row[id]
BEST-PRACTICES-PRINT Utiliser echo qui est plus rapide que print.
NB : echo et print ne sont pas des fonctions mais des structures de langage. Ainsi, il n’est pas nécessaire d’utiliser des parenthèses.
BEST-PRACTICES-COMPUTE-LOOP Éviter de mettre des calculs dans les boucles, utiliser des variables de préférence.Exemple : for ($x=0; $x < count($array); $x)
La fonction count est appelée à chaque boucle.
Il vaut mieux utiliser $max=count($array) pour stocker le résultat du calcul avant la boucle.
BEST-PRACTICES-UNSET Faire un unset ou mettre à NULL les variables qui ne sont plus utilisées, en particulier les gros tableaux.
BEST-PRACTICES-MAGIC-FUNCTION Éviter les méthodes magiques comme __get et __set :
- elles sont plus coûteuses en terme de performance.
- elles constituent une faille de sécurité potentielle car elles exposent tous les attributs de l’objet en visibilité PUBLIC.
BEST-PRACTICES-FILE-INCLUSION Utiliser include (id. require) au lieu de include_once (id. require_once) quand c’est possible.
A chaque include_once (id. require_once), PHP doit vérifier si le fichier a déjà été inclus ou pas. L’étape de vérification ajoute un temps de traitement supplémentaire.
NB : include, include_once, require et require_once sont des instructions et NON des fonctions. Ainsi, le nom du fichier ne doit pas être entouré avec des parenthèses.
BEST-PRACTICES-REQUEST-TIME Utiliser $_SERVER['REQUEST_TIME'] pour récupérer l’heure de démarrage d’un script au lieu de time() ou microtime(). (PHP >= 5)
BEST-PRACTICES-STR-REPLACE Utiliser str_replace pour remplacer les occurrences dans une chaîne.
Il est plus rapide que preg_replace et globalement le meilleur dans tous les cas, même si quelques fois strtr est plus rapide avec des chaines longues.
Utiliser un array() dans str_replace est plus rapide que d’utiliser plusieurs str_replace.
BEST-PRACTICES-SWITCH Utiliser « else if » qui est plus rapide qu’un case/switch.
BEST-PRACTICES-ERROR-CONTROL Éviter la suppression d’erreurs avec @ qui est très lente. Il vaut mieux utiliser error_reporting(0) en début de script.
BEST-PRACTICES-POO Tout ne doit pas forcément être objet : chaque méthode et propriété consomment de la mémoire.
BEST-PRACTICES-STATIC-PAGE Essayer d’utiliser au maximum des pages statiques : les scripts PHP sont rendus 2 à 10 fois moins rapidement par le serveur HTTP qu’une page statique.
BEST-PRACTICES-STRLEN Utiliser isset (ou empty) où c’est possible au lieu de strlen.Exemple : if (!isset($foo{5})) { echo ‘Foo is too short’; } au lieu de if (strlen($foo) < 5) { echo ‘Foo is too short’; }
BEST-PRACTICES-NATIVE-FUNCTION Utiliser les fonctions natives de PHP qui seront toujours plus rapides.
Sinon, privilégier les projets PEAR généralement standards.
BEST-PRACTICES-IP Utiliser ip2long() et long2ip() pour stocker les adresses IP en INT plutôt qu’en STRING.
BEST-PRACTICES-LOCAL-VARIABLE Incrémenter une variable locale est 3 fois plus rapide qu’incrémenter une propriété d’un objet (eg. $this->prop++)
BEST-PRACTICES-GLOBAL-VARIABLE Supprimer les variables globales NON utilisées : déclarer une variable globale dans une fonction sans l’utiliser ralenti inutilement le traitement du script PHP. En effet, les variables globales utilisent les ressources mémoires du serveur.
BEST-PRACTICES-DEFINE-VARIABLE Définir une variable avant de l’utiliser : incrémenter une variable NON définie est 9-10 fois plus lent qu’incrémenter une variable prédéfinie.
BEST-PRACTICES-STATIC-METHOD Privilégier les méthodes en static. Elles sont 4 fois plus rapides.
BEST-PRACTICES-POO-LEGACY Privilégier les appels aux méthodes dérivées : les méthodes d’une classe dérivée vont plus vite que celles de la classe mère.
BEST-PRACTICES-HEADER-LOCATION Ne pas oublier de faire suivre un header(‘Location: ‘.$url); par die(); ou exit; sinon l’exécution du script continue même après l’instruction.
BEST-PRACTICES-POST-FORM Préférer la méthode POST à la méthode GET quand c’est possible. La méthode GET ajoute les données à l’URL ce qui peut constituer un point de vulnérabilité (ex: Cross Site Scripting – XSS).
BEST-PRACTICES-ESCAPE-STRING Ne jamais faire confiance à l’utilisateur et aux données qu’il transmet ($_POST et $_GET). Utiliser mysql_real_escape_string et addcslashes pour l’exécution d’une requête SQL et htmlspecialchars / striptags lors de l’affichage dans une page HTML.
Si magic_quotes_gpc est activé, il vaut mieux utiliser stripslashes en premier lieu (cf. Désactivation de magic_quotes_gpc).
BEST-PRACTICES-MAIL-HEADER Injection de headers dans la fonction mail() : contrôler l’intégrité de la variable
« expéditeur » quand elle est transmise par l’utilisateur.Exemple d’injection :
haxor@attack.com%0AContent-Type:multipart/mixed;%20boundary=frog;%0A–frog%0AContent-Type:text/html%0A%0AMy%20Message.%0A–frog–Exemple de contrôle :
$from=$_POST["expéditeur"];
$from = str_ireplace(array(« \r », « \n », « %0a », « %0d »),  », stripslashes($from));
BEST-PRACTICES-SERVER-CONTROLS Contrôler les données côté serveur : un contrôle des données côté client est bien pratique, mais illusoire quant à la sécurité. En effet, l’utilisateur peut facilement désactiver le JavaScript.
BEST-PRACTICES-ERROR-REPORTING Ne jamais dévoiler d’informations concernant les chemins et configuration (ex: display_errors() ou phpinfo()).
En production, display_errors et error_reporting(E_ALL) doivent être désactivés ==> error_reporting(0);
BEST-PRACTICES-PASSWORD-CRYPT Ne jamais utiliser du texte clair pour stocker les mots de passe ou les comparer. Utilisez un système d’encryptage (ex: hash, md5 au minimum).
Si possible avec un « salt » (grain de sel), aléatoire (de préférence) ou pas.
BEST-PRACTICES-PASSWORD-FORMAT Forcer l’utilisation de mots de passe hautement sécurisés :
- longueur minimale
- présence de lettres ET de chiffres
- présence de minuscules ET de majuscules
BEST-PRACTICES-FILE-UPLOAD-CONTROLS Upload de fichiers :
- vérifier que le contenu du fichier a bien le format attendu (type MIME)
- déplacer le fichier uploadé avec move_uploaded_file()- Renommer les fichiers automatiquement à partir de valeurs aléatoires et
d’extensions imposées
- si c’est une image, la redimensionner pixel par pixel
- désactiver l’interpréteur PHP dans les endroits sensibles (cf. open_basedir)
BEST-PRACTICES-FILE-INCLUSION-CONTROLS Inclusion de fichiers : vérifier le nom et l’existence du fichier. Il doit être inclus dans l’arborescence de l’application et/ou des répertoires cibles.
BEST-PRACTICES-SQL-PREPARED-STATEMENT Injection SQL : ne pas créer des requêtes SQL par concaténation de chaîne de caractères, utiliser le mécanisme de préparation des requêtes avec les variables liées « bind » (ex: PDO).
BEST-PRACTICES-UNINTERPRETED-FILE Interdire l’accès aux fichiers non interprétés : utiliser .htaccess (=>Deny from All ) ou httpd.conf (=>AddType)
NB: éviter de donner l’extension .inc aux fichiers à inclure
BEST-PRACTICES-SQL-DATABASE-LOGIN L’application doit se connecter au serveur de bases de données avec un profil utilisateur dont les droits sont restreints au strict minimum (lecture seule si possible).
Ne JAMAIS se connecter avec le compte du propriétaire / administrateur.
NB : il est recommandé de créer différents utilisateurs de bases de données pour chaque aspect de l’application, avec des droits limités aux seules actions planifiées. Il faut alors éviter que le même utilisateur dispose des droits de plusieurs cas d’utilisation.
BEST-PRACTICES-SQL-USER-ACCOUNTS Ne JAMAIS laisser le login / mot de passe d’un administrateur en premier enregistrement d’une table. Si le code présente un risque d’injection SQL alors le pirate peut accéder aux données d’accès et s’authentifier en tant qu’administrateur.
BEST-PRACTICES-SQL-DATABASE-LOGOUT Fermer les connexions aux SGBD (Systèmes de Gestion de Bases de Données) après les avoir utilisées.
BEST-PRACTICES-SQL-DATABASE-SSL Privilégier les connexions au serveur avec le protocole SSL pour chiffrer les échanges clients/serveur et/ou l’utilisation d’un client SSH pour chiffrer la connexion entre les clients et le serveur de base de données.
BEST-PRACTICES-SECURITY-FLAW Eviter les fonctions ciblées par les failles de sécurité ou y ajouter des contrôles supplémentaires :Execution du code PHP :
- require et include : lire un fichier spécifié et interpréter le contenu comme du code PHP
-eval() : interpréter une chaîne donnée comme du code PHP
- preg_replace() : utilisée avec le modificateur /e, cette fonction interprète la chaîne de remplacement comme du code PHPExecution de commandes :
- exec() : exécuter une commande spécifiée et retourner la dernière ligne résultante de cette commande.
- passthru() : exécuter une commande
spécifiée et retourner tous les résultats directement dans le browser
- «  (apostrophes inversées) : Exécuter la commande spécifiée et retourner tous les résultats directement dans un tableau
- system() : un peu la même chose que passthru() mais ne gère pas les donnéesBinaires :
- popen() : exécuter une commande spécifiée et connecter le flux de sortie vers un descripteur de fichier PHP

Manipulation de fichiers:
- fopen() : ouvrir un fichier et l’associer avec un descripteur de fichier PHP
- readfile() : lire un fichier et écrire son contenu dans le browser
- file() : lire un fichier et mettre son contenu et ses infos dans un tableau

BEST-PRACTICES-CSRF-XSS Préférer le stockage des données des utilisateurs en SESSION plutôt que dans les COOKIES
- COOKIES : les données résident côté client. Ils peuvent être forgés (créés de toute pièce), modifiés ou supprimés par le client
- SESSION : les données résident sur le serveur, le client n’y a pas accès
BEST-PRACTICES-SESSION-REGENERATE-ID Changer l’ID de session : utiliser la fonction session_regenerate_id() lorsqu’un utilisateur se connecte afin de limiter le vol de session.

Normes de codage

ID Description
CODING-CONVENTIONS-STANDARD Utiliser les standards pour une meilleure compréhension du code par les autres.
CODING-CONVENTIONS-CLASS-NAME Classes et Interfaces : PascalCase (UpperCamelCase)
Les noms de classe doivent correspondre au nom du répertoire sur le système de fichier qui contient le fichier de classe.
Les noms doivent être alphanumériques uniquement, à l’exception d’un ‘_’, qui doit être utilisé pour montrer la séparation dans le chemin.
Si vous avez une classe fichier nommé Car.php dans le chemin ‘/ Modèles / Car.php’ alors le nom de la classe devra être Models_Car.
CODING-CONVENTIONS-FUNCTION-NAME Fonctions, Méthodes et Variables : camelCase (lowerCamelCase)
Des alphanumériques uniquement, à l’exception de ‘_’ comme décrit ici.
Le nom doit décrire le comportement de la fonction ou de la méthode / les données dans la variable.
Les méthodes / variables qui sont déclarées avec modificateur privé ou protégé de visibilité doivent commencer par un _.
CODING-CONVENTIONS-CONSTANT-NAME Constantes : ALL_CAPS
Des alphanumériques et ‘_’ sont autorisés cependant ‘_’ doit être utilisé pour séparer les mots dans des constantes.
CODING-CONVENTIONS-CLASS-FILE Utiliser un fichier PHP par classe.
CODING-CONVENTIONS-LINE Faciliter la lecture du code :
- Classes : 10-20 méthodes
- Méthodes : 30-50 lignes
- Fichiers : moins de 3000 lignes
- Lignes : moins de 75-85 caractères
CODING-CONVENTIONS-INDENT Indenter le code : facilite la lecture et la compréhension du code.
CODING-CONVENTIONS-COMMENT Documenter le code : utiliser les commentaires pour expliquer le code et le fonctionnement du programme.
CODING_CONVENTIONS-STRUCTURE Structures de contrôle :
- mettre un espace entre le mot clé de l’instruction et la parenthèse ouvrante afin de les distinguer des appels de fonctions
- toujours utiliser des accolades, même dans les situations où elles sont techniquement optionnelles, pour augmenter la lisibilité

Fichier php.ini

ID Description
PHP-INI-CONFIGURATION Activation et configuration de safe_mode
NB: Cette fonctionnalité est devenue OBSOLETE depuis PHP 5.3.0 et a été SUPPRIMÉE depuis PHP 5.4.
Activation et configuration de open_basedir
Activation de log_errors
Désactivation de display_errors
Désactivation de allow_url_include
Désactivation de allow_url_fopen
Désactivation de register_globals
NB: fonction OBSOLETE depuis PHP 5.3.0 et SUPPRIMÉE depuis PHP 5.4.0.
Désactivation de magic_quotes_gpc
NB: fonction OBSOLETE depuis PHP 5.3.0 et a été SUPPRIMÉE depuis PHP 5.4.0.
Désactivation de short_open_tag

 


Rendu :0.0979 | Mémoire :2.93MB

Accueil | Informations | Top