# Les fonctions de sécurité intégrées de PHP

> Les fonctions de sécurité de PHP en un coup d'œil : mots de passe, aléa, échappement, cryptographie, sessions et leurs limites face à des failles comme PHP2Shell.

Source : https://phpsos.de/fr/php/eingebaute-sicherheitsfunktionen/  
État : 25/10/2026  
Fournisseur : PHP SOS, Assistance d'urgence PHP et sécurité des serveurs. Aide immédiate par WhatsApp : https://wa.me/491736494914?text=Bonjour%20PHP%20SOS%2C%20nous%20avons%20besoin%20d%27aide%20avec%20notre%20serveur%20PHP.%20Entreprise%20%3A%20%E2%80%A6%20Site%20web%20%3A%20%E2%80%A6

**En bref :** pour les tâches de sécurité les plus courantes, PHP fournit des fonctions toutes prêtes et éprouvées : `password_hash()` pour les mots de passe, `random_bytes()` et `random_int()` pour les jetons, `htmlspecialchars()` contre le cross-site scripting, les requêtes préparées avec PDO contre l'injection SQL, `hash_equals()` pour des comparaisons sûres et l'extension Sodium pour la cryptographie. Qui utilise systématiquement ces briques évite la plupart des failles d'application. Contre les erreurs de l'interpréteur PHP lui-même, comme avec PHP2Shell, elles n'aident pas ; seule la mise à jour y remédie.

## Pourquoi des fonctions de sécurité intégrées à PHP ?

Écrire soi-même du code de sécurité est source d'erreurs. Un hachage de mot de passe maison, un générateur de jetons bricolé ou un filtre artisanal pour les sorties HTML semblent souvent corrects et ne le sont pourtant pas. Les fonctions intégrées sont documentées, testées et maintenues avec les mises à jour de PHP.

Pour les décideurs, cela signifie : ne demandez pas à votre agence ou à votre équipe de développement si des fonctions de sécurité ont été développées en interne, mais si les fonctions standard du langage sont utilisées. Le tableau en donne un aperçu.

| Tâche | Fonction | Disponible depuis | Remarque importante |
|---|---|---|---|
| Stocker des mots de passe | `password_hash()`, `password_verify()` | PHP 5.5 | colonne jusqu'à 255 caractères ; bcrypt tronque à 72 octets |
| Jetons aléatoires | `random_bytes()`, `random_int()` | PHP 7.0 | jamais `rand()` ni `mt_rand()` pour des secrets |
| Aléa orienté objet | `Random\Randomizer` | PHP 8.2 | même sécurité, interface plus flexible |
| Échapper la sortie HTML | `htmlspecialchars()` | depuis toujours | les drapeaux par défaut depuis 8.1 échappent aussi les apostrophes |
| Comparaison à temps constant | `hash_equals()` | PHP 5.6 | saisie de l'utilisateur en second argument |
| Cryptographie moderne | Sodium (`sodium_*`) | PHP 7.2 | plutôt qu'un chiffrement maison |
| Secrets dans les traces d'appels | `#[\SensitiveParameter]` | PHP 8.2 | la valeur n'apparaît pas dans la trace |
| Vérifier les saisies | `filter_var()` | PHP 5.2 | vérifie le format, ne remplace pas l'échappement |

## Mots de passe et jetons

`password_hash()` produit un hachage salé, `password_verify()` le vérifie. Avec `PASSWORD_DEFAULT`, PHP utilise actuellement bcrypt ; depuis PHP 8.4, le facteur de coût par défaut est, selon le manuel, de 12 au lieu de 10. Comme l'algorithme par défaut peut changer, le manuel recommande une colonne de base de données de 255 caractères. Avec bcrypt, les mots de passe sont tronqués après 72 octets, ce qu'il faut garder à l'esprit pour de très longues phrases de passe.

```php
<?php
// Inscription
$hash = password_hash($passwort, PASSWORD_DEFAULT);

// Connexion, avec mise à niveau automatique des anciens hachages
if (password_verify($eingabe, $hash)) {
    if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
        $hash = password_hash($eingabe, PASSWORD_DEFAULT); // enregistrer à nouveau
    }
}

// Jeton pour un lien de réinitialisation du mot de passe
$token = bin2hex(random_bytes(32));
```

Pour comparer des secrets, par exemple une signature, il faut utiliser `hash_equals()`. Une comparaison normale avec `===` peut, selon le manuel, révéler par son temps d'exécution à partir de quel endroit deux valeurs diffèrent. Plus de détails : [Stocker les mots de passe en PHP en toute sécurité](https://phpsos.de/fr/blog/passwoerter-in-php-speichern/) et [nombres aléatoires et jetons sûrs](https://phpsos.de/fr/blog/sichere-tokens-php/).

## Échapper les sorties et interroger la base de données en sécurité

Le cross-site scripting naît lorsque des saisies d'utilisateurs aboutissent sans filtre dans du HTML. `htmlspecialchars()` convertit les caractères spéciaux en entités HTML. Depuis PHP 8.1, les drapeaux par défaut sont `ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401` ; auparavant, les apostrophes n'étaient pas converties. Qui doit prendre en charge des versions plus anciennes indique les drapeaux explicitement.

L'injection SQL est empêchée par les requêtes préparées. Les valeurs sont transmises séparément du texte SQL et ne peuvent pas modifier la requête :

```php
<?php
$stmt = $pdo->prepare('SELECT id, name FROM kunden WHERE email = :email');
$stmt->execute(['email' => $email]);
$kunde = $stmt->fetch();

echo htmlspecialchars($kunde['name'] ?? '', ENT_QUOTES, 'UTF-8');
```

Les détails figurent sous [Empêcher le cross-site scripting en PHP](https://phpsos.de/fr/ratgeber/xss-php/) et dans la page consacrée à l'injection SQL.

Vérifier les saisies est une seconde étape, distincte. `filter_var()` détermine si une valeur a un format valide, par exemple une adresse e-mail ou un entier dans une plage autorisée. Cela ne remplace toutefois ni l'échappement à la sortie ni une requête préparée, car des valeurs formellement valides peuvent aussi contenir des caractères spéciaux.

## Garder les secrets hors des messages d'erreur

Les traces d'appels sont une fuite de données sous-estimée. Lorsqu'une erreur survient dans une fonction, PHP consigne la chaîne d'appels avec les arguments. Si un mot de passe ou une clé d'API s'y trouve, il aboutit dans le log. Depuis PHP 8.2, l'attribut `#[\SensitiveParameter]` marque de tels paramètres ; leur valeur n'apparaît alors dans la trace que sous la forme d'un objet `SensitiveParameterValue`.

```php
<?php
function anmelden(string $benutzer, #[\SensitiveParameter] string $passwort): bool
{
    // ... vérification ...
    throw new RuntimeException('Datenbank nicht erreichbar');
}
```

Selon une remarque du manuel, l'attribut n'agit que sur l'implémentation concrète, non sur la méthode d'une interface. Placez-le donc là où le code s'exécute réellement.

## Sessions : réglages sûrs et nouvelles valeurs par défaut

Les sessions sont une cible fréquente d'attaque. Trois réglages sont centraux : `session.use_strict_mode=1` rejette les identifiants de session que le serveur n'a pas générés lui-même ; `session.cookie_httponly=1` empêche l'accès au cookie de session par JavaScript ; `session.cookie_samesite=Lax` rend plus difficile son envoi depuis des sites tiers. Selon la RFC adoptée, ces trois valeurs deviendront précisément la valeur par défaut avec PHP 8.6. Définissez-les vous-même d'ici là, et aussi ensuite de façon explicite, afin que la configuration ne dépende pas de la valeur par défaut.

```ini
session.use_strict_mode = 1
session.cookie_httponly = 1
session.cookie_samesite = Lax
session.cookie_secure = 1
```

Plus de détails sous [Sécuriser les sessions en PHP](https://phpsos.de/fr/ratgeber/session-sicherheit-php/).

## Ce que les fonctions intégrées ne font pas

Le projet PHP est clair dans sa politique de sécurité : PHP n'est pas un bac à sable. Les contournements de `open_basedir` ou de `disable_functions`, les entrées malveillantes transmises à `unserialize()` et les arguments non vérifiés passés à des fonctions comme `mysqli_query()` ne sont pas considérés comme des failles de sécurité de PHP. Pour les exploitants, cela signifie : ces réglages sont un durcissement utile, mais pas une frontière de sécurité, et `unserialize()` n'a rien à faire avec des données externes.

Le second point concerne PHP2Shell. Selon php.net, CVE-2026-109618 et CVE-2026-109619 permettent l'exécution de code avant ou pendant l'exécution de votre application ; avec mod_php, un exploit a été démontré avant même que le code de l'application ne s'exécute. Aucune fonction de votre code ne peut intercepter cela. Une programmation sûre et une version de PHP à jour sont deux obligations distinctes, et toutes deux doivent être remplies.

## Comment PHP SOS aide-t-il concrètement ?

Dans l'[audit de sécurité des applications PHP](https://phpsos.de/fr/leistungen/sicherheitsaudit-penetrationstest/), nous vérifions de façon ciblée :

- **Gestion des mots de passe et des jetons :** `password_hash()` et `random_bytes()` sont-ils utilisés, ou existe-t-il des développements maison ?
- **Sorties et requêtes :** où des saisies aboutissent-elles sans filtre dans du HTML, du SQL ou des commandes shell ?
- **Configuration des sessions :** le mode strict, HttpOnly, SameSite et Secure sont-ils définis et non redéfinissables ?
- **Exécution :** une version de PHP prise en charge tourne-t-elle avec les corrections du 21 octobre 2026 ?


## Questions fréquentes

**Comment stocker correctement les mots de passe en PHP ?**  
Avec password_hash() et PASSWORD_DEFAULT ; la vérification se fait avec password_verify(). Selon le manuel, la colonne de la base de données devrait pouvoir contenir jusqu'à 255 caractères, car l'algorithme par défaut peut changer à l'avenir.

**rand() convient-il pour des jetons ?**  
Non. Pour les jetons, les clés et les liens de mot de passe, il faut utiliser random_bytes() ou random_int(). Selon le manuel, elles fournissent des valeurs aléatoires cryptographiquement sûres.

**Depuis quand Sodium est-il inclus dans PHP ?**  
L'extension Sodium pour la cryptographie moderne fait partie de PHP depuis PHP 7.2.0. Les versions plus anciennes ne pouvaient l'ajouter qu'après coup via PECL.

**Ces fonctions protègent-elles contre PHP2Shell ?**  
Non. Elles protègent contre les erreurs de votre application. PHP2Shell se trouve dans l'interpréteur lui-même et ne se comble que par une mise à jour de PHP, suivie du redémarrage de tous les processus PHP.

**Les valeurs par défaut des sessions changent-elles dans PHP 8.6 ?**  
Oui, selon la RFC adoptée, session.use_strict_mode et session.cookie_httponly sont activés par défaut et session.cookie_samesite est défini sur Lax. Vous devriez néanmoins définir vous-même explicitement les configurations existantes.

## Sources

- [Manuel PHP : password_hash()](https://www.php.net/manual/en/function.password-hash.php)
- [Manuel PHP : random_bytes()](https://www.php.net/manual/en/function.random-bytes.php)
- [Manuel PHP : hash_equals()](https://www.php.net/manual/en/function.hash-equals.php)
- [RFC PHP : Secure Session Configuration Defaults](https://wiki.php.net/rfc/session_security_defaults)
- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
