🍪 Cookies & Sessions

Garder les données utilisateur

Formation DWWM - Gestion d'état en PHP — 2025/2026

Au programme aujourd'hui

  • 🔷 Superglobales PHP : $_SESSION, $_COOKIE et les autres NOUVEAU
  • 🍪 Cookies : créer, modifier, supprimer
  • 📦 Sessions : démarrer, utiliser, gérer
  • 🔐 Sécurité : HTTP Only, Secure, SameSite
  • ⚖️ RGPD / CNIL : obligations légales
  • 🔑 Authentification : système de login complet
  • 💪 Exercices : pratique et mise en situation
🔷

Partie 0

Les Superglobales PHP

La mémoire de PHP à portée de variable

Qu'est-ce qu'une superglobale ? 🔷

Superglobale = Variable spéciale prédéfinie par PHP, accessible partout dans le code sans avoir à l'importer ou la passer en paramètre — même à l'intérieur des fonctions.

Variable classique ❌

<?php
function maFonction() {
    // ❌ $nom défini hors de la
    //    fonction : inaccessible
    echo $nom; // Warning: Undefined
}
$nom = "Marie";
maFonction();

Superglobale ✅

<?php
function maFonction() {
    // ✅ $_SESSION accessible
    //    partout, même ici !
    echo $_SESSION['nom']; // Marie
}
session_start();
$_SESSION['nom'] = "Marie";
maFonction();
💡 Le préfixe $_ (dollar + underscore) identifie toutes les superglobales PHP. Elles sont toujours écrites en MAJUSCULES.

Les 9 superglobales PHP 📋

$_SESSION
📦 Données stockées côté serveur, persistantes entre les pages
$_COOKIE
🍪 Données stockées dans le navigateur de l'utilisateur
$_GET
🔗 Données passées dans l'URL (après le ?)
$_POST
📋 Données envoyées par un formulaire (méthode POST)
$_SERVER
🖥️ Infos sur le serveur et la requête HTTP en cours
$_FILES
📎 Fichiers envoyés via un formulaire upload
$_ENV
🌍 Variables d'environnement du système
$GLOBALS
🌐 Toutes les variables globales du script courant
$_REQUEST
🔀 Fusion de $_GET + $_POST + $_COOKIE

🔶 Les cases orangées = superglobales du cours d'aujourd'hui

Les superglobales en pratique 🛠️

<?php
// $_GET — Récupérer une valeur dans l'URL
// URL : monsite.com/page.php?couleur=bleu
$couleur = $_GET['couleur'];          // → "bleu"

// $_POST — Récupérer un champ de formulaire (method="POST")
$email = $_POST['email'];             // → valeur du champ email

// $_SERVER — Infos sur la requête en cours
$url     = $_SERVER['REQUEST_URI'];   // → "/page.php?couleur=bleu"
$methode = $_SERVER['REQUEST_METHOD']; // → "GET" ou "POST"
$ip      = $_SERVER['REMOTE_ADDR'];   // → "192.168.1.1"

// $_COOKIE — Lire un cookie (créé par setcookie() lors d'une visite précédente)
$theme = $_COOKIE['theme'];           // → "dark"

// $_SESSION — Lire une donnée de session (après session_start())
session_start();
$nom = $_SESSION['nom'];              // → "Marie"

// Ce sont tous des tableaux associatifs : clé => valeur
?>
💡 Ces variables sont toutes des tableaux associatifs (clé → valeur). On accède à leur contenu avec la notation $_VAR['cle'].

⚠️ Superglobales : règles de sécurité

Règle d'or : Ne jamais faire confiance aux données venant de l'utilisateur !
$_GET, $_POST, $_COOKIE = données contrôlées par l'utilisateur = potentiellement dangereuses
<?php
// ❌ DANGEREUX : afficher directement une superglobale
echo $_GET['nom'];   // Faille XSS ! L'utilisateur peut injecter du JS

// ✅ SÉCURISÉ : Nettoyer avant d'afficher
echo htmlspecialchars($_GET['nom']); // Échappe < > & " '

// ✅ SÉCURISÉ : Vérifier que la clé existe avant d'y accéder
$nom = $_POST['nom'] ?? '';          // Si absent → chaîne vide
$age = (int)($_GET['age'] ?? 0);    // Cast en entier pour sécuriser

// ✅ SÉCURISÉ : Valider les valeurs acceptables (liste blanche)
$languesValides = ['fr', 'en', 'es'];
if (in_array($_GET['lang'] ?? '', $languesValides)) {
    $langue = $_GET['lang'];
}
?>
⚠️ Les données de $_SESSION sont plus sûres (stockées côté serveur), mais doivent tout de même être validées avant insertion en base de données.
🎯

Partie 1

Introduction : Cookies vs Sessions

Pourquoi garder des données ?

Le problème : HTTP est sans état 🤔

HTTP = Protocole sans mémoire
Chaque requête est indépendante. Après avoir répondu, le serveur "oublie" tout ce qui s'est passé. Il ne reconnaît pas l'utilisateur à la page suivante.

Sans persistance ❌

  • L'utilisateur se connecte → page suivante : plus connecté
  • Ajoute un article au panier → recharge : panier vide
  • Change la langue → page suivante : langue par défaut

Avec Cookies/Sessions ✅

  • La connexion est maintenue sur toutes les pages
  • Le panier conserve les articles
  • Les préférences persistent
💡 Solution : Cookies et Sessions permettent de simuler une "mémoire" malgré le caractère sans état de HTTP.

Qu'est-ce qu'un Cookie ? 🍪

Cookie = Petit fichier texte (max ~4 Ko) stocké dans le navigateur. Le serveur le crée via un en-tête HTTP, le navigateur le conserve et le renvoie automatiquement à chaque nouvelle requête vers ce serveur.
Analogie du badge visiteur :
🏢 Vous entrez dans une entreprise → 🎫 On vous donne un badge visiteur → 🚪 Le badge vous reconnaît à chaque porte → 👋 En partant, vous rendez le badge.

Le cookie, c'est pareil : le serveur "donne" un badge (cookie) au navigateur pour le reconnaître à chaque nouvelle visite.

Qu'est-ce qu'une Session ? 📦

Session = Données stockées sur le serveur, identifiées par un ID unique (PHPSESSID) transmis via un petit cookie. L'utilisateur ne voit jamais vos données — seulement son "numéro de casier".
Analogie du casier de piscine :
🏊 Vous arrivez à la piscine → 🗝️ On vous donne la clé du casier n°42 → 🎒 Vos affaires restent dans le casier (sur place, vous ne les emportez pas) → La clé seule permet de les récupérer.

La session, c'est pareil : le cookie ne contient qu'un numéro (la clé), les vraies données restent sur le serveur (le casier).

Cookies vs Sessions : Schéma 📊

🍪 COOKIE

NAVIGATEUR
Cookie: nom=Marie
Cookie: theme=dark
Cookie: lang=fr

✅ Stocké côté client
📦 Données limitées (~4 Ko)
👁️ Visible par l'utilisateur

📦 SESSION

NAVIGATEUR
Cookie: PHPSESSID=abc123
↓ envoie l'ID à chaque requête
SERVEUR
Session abc123:
nom: Marie
role: admin
panier: [...]

Tableau comparatif complet 📋

Critère🍪 Cookie📦 Session
StockageNavigateur (client)Serveur
Taille max~4 Ko par cookieIllimitée (RAM serveur)
Durée de vieConfigurable (jours/mois)Fin de navigation
Sécurité⚠️ Moins sûr (côté client)🛡️ Plus sûr (côté serveur)
Accessible JSOui (sauf HTTP Only)Non
Bande passanteEnvoyé à chaque requêteJuste l'ID envoyé
UsagePréférences, "Se souvenir"Authentification, panier

Quand utiliser quoi ? 🎯

🍪 Utilisez les COOKIES pour :

  • ✅ Préférences utilisateur (langue, thème)
  • ✅ "Se souvenir de moi" (login persistant)
  • ✅ Tracking / Analytics
  • ✅ Données non sensibles
  • ✅ Conservation longue durée (30 jours+)

📦 Utilisez les SESSIONS pour :

  • ✅ Authentification (utilisateur connecté)
  • ✅ Panier d'achat
  • ✅ Données sensibles
  • ✅ Formulaires multi-étapes
  • ✅ Données temporaires de navigation
📦

Partie 2

Les Sessions PHP

Stocker des données sur le serveur

Comment fonctionne une session ? 🔄

Étape 1 : Première visite

👤 Utilisateur visite le site
↓
🖥️ Serveur crée une session
ID unique: abc123
↓
📦 Serveur stocke:
$_SESSION['nom'] = 'Marie'
↓
🍪 Cookie envoyé au navigateur:
PHPSESSID=abc123
↓
💾 Navigateur stocke ce cookie

Étape 2 : Pages suivantes

👤 Nouvelle page demandée
↓
🍪 Navigateur renvoie:
PHPSESSID=abc123
↓
🖥️ Serveur retrouve la session
avec l'ID abc123
↓
📦 PHP charge dans $_SESSION:
$_SESSION['nom'] → 'Marie'
↓
✅ L'utilisateur est reconnu !

La superglobale $_SESSION en détail 🔷

$_SESSION est un tableau associatif (clé → valeur) géré automatiquement par PHP. Son contenu est écrit dans un fichier temporaire sur le serveur, et rechargé à chaque appel de session_start().
<?php
session_start(); // ← PHP charge $_SESSION depuis le fichier serveur

// $_SESSION se comporte exactement comme un tableau PHP classique
$_SESSION['nom']  = 'Marie';        // Écrire une valeur
echo $_SESSION['nom'];              // Lire → "Marie"
unset($_SESSION['nom']);            // Supprimer une clé
echo isset($_SESSION['nom']);       // Vérifier → false

// On peut stocker n'importe quel type PHP
$_SESSION['age']      = 25;         // Entier
$_SESSION['connecte'] = true;       // Booléen
$_SESSION['panier']   = [1, 2, 3];  // Tableau
// PHP sérialise automatiquement les données complexes
?>
💡 PHP écrit automatiquement les modifications de $_SESSION dans un fichier sur le serveur à la fin de l'exécution du script. Rien à faire manuellement !

Démarrer une session : session_start() 🚀

session_start() : Démarre une nouvelle session OU reprend une session existante si le cookie PHPSESSID est déjà présent dans la requête. PHP charge alors automatiquement les données dans $_SESSION.
<?php
// ⚠️ TOUJOURS en tout début de fichier, AVANT tout HTML ou echo
session_start();

// $_SESSION est maintenant disponible !
$_SESSION['nom']  = 'Marie';
$_SESSION['role'] = 'admin';

?>
<!DOCTYPE html>
<html>
<body>
    <h1>Bonjour <?= $_SESSION['nom'] ?></h1>
    <!-- Affiche : Bonjour Marie -->
</body>
</html>
❌ CRITIQUE : session_start() doit être appelé AVANT tout echo, HTML, ou espace avant <?php. Sinon → erreur "Cannot modify header information - headers already sent".

Stocker des données : $_SESSION 💾

<?php
session_start();

// Valeurs simples
$_SESSION['prenom']   = 'Marie';
$_SESSION['age']      = 25;
$_SESSION['connecte'] = true;

// Tableau (panier d'achat)
$_SESSION['panier'] = [
    ['id' => 1, 'nom' => 'Ordinateur', 'prix' => 899],
    ['id' => 2, 'nom' => 'Souris', 'prix' => 25]
];

// Tableau imbriqué (infos utilisateur)
$_SESSION['utilisateur'] = [
    'id'    => 42,
    'nom'   => 'Dupont',
    'email' => '[email protected]',
    'role'  => 'admin'
];

// Compteur de visites — opérateur ?? (coalescence null)
// Si 'visites' n'existe pas → 0, sinon valeur existante + 1
$_SESSION['visites'] = ($_SESSION['visites'] ?? 0) + 1;
?>

Lire et utiliser $_SESSION 📖

<?php
session_start();

// Toujours vérifier avec isset() avant de lire
if (isset($_SESSION['prenom'])) {
    echo "Bonjour " . $_SESSION['prenom'];
} else {
    echo "Vous n'êtes pas connecté";
}

// Version courte avec l'opérateur ?? (valeur par défaut)
$prenom = $_SESSION['prenom'] ?? 'Visiteur';
echo "Bonjour $prenom"; // "Bonjour Visiteur" si clé absente

// Accès à un tableau imbriqué
$nbArticles = count($_SESSION['panier'] ?? []);
echo "Panier : $nbArticles article(s)";

// Vérifier le rôle
if (isset($_SESSION['utilisateur'])) {
    echo "Bienvenue " . $_SESSION['utilisateur']['nom'];
    if ($_SESSION['utilisateur']['role'] === 'admin') {
        echo " (Administrateur)";
    }
}
?>
💡 Utilisez toujours isset() ou ?? avant de lire une clé. Sans ça → Warning "Undefined index" si la clé n'existe pas.

Modifier des données de session ✏️

<?php
session_start();

// 1. Modifier une valeur simple
$_SESSION['theme'] = 'dark'; // Remplace 'light' par 'dark'

// 2. Ajouter un article au panier (tableau)
$nouvelArticle = ['id' => 3, 'nom' => 'Clavier', 'prix' => 45];
if (!isset($_SESSION['panier'])) {
    $_SESSION['panier'] = []; // Initialiser si absent
}
$_SESSION['panier'][] = $nouvelArticle; // [] = ajouter à la fin

// 3. Supprimer un élément du tableau (par index)
unset($_SESSION['panier'][1]); // Supprime l'index 1

// 4. Modifier une valeur dans un tableau imbriqué
$_SESSION['utilisateur']['email'] = '[email protected]';

// 5. Incrémenter un compteur
$_SESSION['compteur_connexions']++;

// 6. Ajouter à un historique
$_SESSION['historique'][] = "Page consultée à " . date('H:i:s');
?>

Supprimer des données de session 🗑️

<?php
session_start();

// 1. Supprimer UNE variable de session
unset($_SESSION['panier']);
// → La clé 'panier' n'existe plus dans $_SESSION

// 2. Vider TOUTES les variables (la session continue d'exister)
$_SESSION = [];
// → Le tableau est vide, mais le fichier session existe encore

// 3. Détruire la session sur le serveur
session_destroy();
// ⚠️ Attention : le cookie PHPSESSID existe encore dans le navigateur !

// ✅ DÉCONNEXION COMPLÈTE (les 3 étapes obligatoires)
$_SESSION = [];                                   // 1. Vider les données
session_destroy();                                // 2. Supprimer le fichier serveur
setcookie('PHPSESSID', '', time()-3600, '/');     // 3. Supprimer le cookie navigateur
?>
💡 Pour une déconnexion propre et sécurisée, effectuez toujours ces 3 étapes dans l'ordre.

Exemple complet : Système de login 🔐

<?php // login.php
session_start();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $email    = $_POST['email'] ?? '';
    $password = $_POST['password'] ?? '';

    // Vérification BDD (simulée ici)
    if ($email === '[email protected]' && $password === '123456') {
        session_regenerate_id(true); // ← Sécurité : nouveau ID de session
        $_SESSION['utilisateur'] = [
            'id'    => 1,
            'nom'   => 'Marie Dupont',
            'email' => $email,
            'role'  => 'admin'
        ];
        header('Location: dashboard.php');
        exit;
    } else {
        $erreur = "Identifiants incorrects";
    }
}
?>
<form method="POST">
    <?php if (isset($erreur)): ?>
        <p style="color:red;"><?= $erreur ?></p>
    <?php endif; ?>
    <input type="email" name="email" required>
    <input type="password" name="password" required>
    <button type="submit">Connexion</button>
</form>

Page protégée : vérifier la session 🛡️

<?php // dashboard.php
session_start();

// Vérifier si l'utilisateur est connecté
if (!isset($_SESSION['utilisateur'])) {
    header('Location: login.php'); // Non connecté → redirection
    exit;
}

$user = $_SESSION['utilisateur'];
?>
<!DOCTYPE html>
<html>
<body>
    <h1>Dashboard de <?= htmlspecialchars($user['nom']) ?></h1>
    <p>Email : <?= htmlspecialchars($user['email']) ?></p>

    <?php if ($user['role'] === 'admin'): ?>
        <h2>Panel Administration</h2>
    <?php endif; ?>

    <a href="logout.php">Déconnexion</a>
</body>
</html>
💡 htmlspecialchars() est indispensable pour éviter les failles XSS lors de l'affichage de données.

Déconnexion complète 👋

<?php // logout.php
session_start();

// 1. Vider toutes les variables de session
$_SESSION = [];

// 2. Supprimer le cookie de session dans le navigateur
if (isset($_COOKIE['PHPSESSID'])) {
    setcookie('PHPSESSID', '', time() - 3600, '/');
}

// 3. Détruire la session sur le serveur
session_destroy();

// 4. Rediriger
header('Location: login.php');
exit;
?>
✅ Ces 3 étapes garantissent une déconnexion propre :
1️⃣ $_SESSION = [] → données effacées en mémoire
2️⃣ session_destroy() → fichier de session supprimé sur le serveur
3️⃣ setcookie(…) → cookie PHPSESSID supprimé du navigateur

Configuration avancée de la session ⚙️

<?php
// Tout ceci AVANT session_start()

ini_set('session.cookie_lifetime', 86400); // Cookie dure 24h
ini_set('session.gc_maxlifetime', 86400);  // Fichier session dure 24h

session_name('MON_SITE_SESSION');          // Renommer PHPSESSID

ini_set('session.cookie_httponly', 1);     // Pas accessible en JS
ini_set('session.cookie_secure', 1);       // HTTPS uniquement
ini_set('session.cookie_samesite', 'Strict'); // Protection CSRF

session_start();

// Régénérer l'ID de session régulièrement (anti-hijacking)
session_regenerate_id(true); // Nouveau ID + supprime l'ancien
?>
🍪

Partie 3

Les Cookies PHP

Stocker des données dans le navigateur

La superglobale $_COOKIE en détail 🔷

$_COOKIE est un tableau associatif rempli automatiquement par PHP avec tous les cookies que le navigateur a envoyés dans la requête HTTP courante. Il est en lecture seule : pour créer ou modifier un cookie, on utilise setcookie().
<?php
// $_COOKIE contient les cookies REÇUS du navigateur
// (ceux créés lors d'une visite précédente avec setcookie())

var_dump($_COOKIE);
// Exemple de résultat :
// array(3) {
//   ["langue"]    => string(2) "fr"
//   ["theme"]     => string(4) "dark"
//   ["PHPSESSID"] => string(26) "abc123xyz..."
// }

// Lire un cookie avec valeur par défaut si absent
$langue = $_COOKIE['langue'] ?? 'fr';
?>
⚠️ Important : Si vous créez un cookie avec setcookie(), il n'apparaît dans $_COOKIE qu'au prochain chargement de page (le navigateur doit d'abord le recevoir, puis le renvoyer).

Créer un cookie : setcookie() 🍪

<?php
// ⚠️ AVANT tout HTML (comme session_start)

// Syntaxe simple (déconseillée en production)
setcookie('nom', 'valeur');

// Syntaxe tableau recommandée (PHP 7.3+)
setcookie('nom_cookie', 'valeur', [
    'expires'  => time() + 3600, // Dans 1 heure
    'path'     => '/',           // Tout le site
    'secure'   => true,          // HTTPS uniquement
    'httponly' => true,          // Pas accessible en JS
    'samesite' => 'Lax'          // Protection CSRF
]);

// Exemples concrets
setcookie('langue', 'fr', time() + 86400 * 30);  // 30 jours
setcookie('theme', 'dark', time() + 86400 * 365); // 1 an
?>
💡 time() retourne le timestamp UNIX actuel (secondes depuis le 1er janvier 1970). On y ajoute des secondes pour définir l'expiration du cookie.

Durée d'expiration : calculs utiles ⏱️

<?php
// Mémo des durées en secondes :
// 60       = 1 minute
// 3600     = 1 heure     (60 × 60)
// 86400    = 1 jour      (60 × 60 × 24)

setcookie('test', 'val', time() + 3600);          // 1 heure
setcookie('test', 'val', time() + 86400);          // 1 jour
setcookie('test', 'val', time() + (86400 * 7));    // 1 semaine
setcookie('test', 'val', time() + (86400 * 30));   // 30 jours (~1 mois)
setcookie('test', 'val', time() + (86400 * 365));  // 1 an

// Cookie de session = supprimé à la fermeture du navigateur
setcookie('test', 'valeur', 0);
// Ou sans 3e paramètre :
setcookie('test', 'valeur');
?>

Lire un cookie : $_COOKIE 📖

<?php
// Lire avec vérification
if (isset($_COOKIE['langue'])) {
    $langue = $_COOKIE['langue'];
    echo "Langue : $langue";
} else {
    echo "Cookie 'langue' non défini";
}

// Version courte avec opérateur ??
$langue = $_COOKIE['langue'] ?? 'fr';
$theme  = $_COOKIE['theme']  ?? 'light';

// Compteur de visites
$visites = (int)($_COOKIE['compteur_visites'] ?? 0);
$visites++;
setcookie('compteur_visites', $visites, time() + 86400 * 30);
echo "Vous avez visité ce site $visites fois";

// Debug : afficher tous les cookies reçus
var_dump($_COOKIE);
?>

Modifier et supprimer un cookie 🗑️

<?php
// MODIFIER : recréer avec le même nom, nouvelle valeur
setcookie('theme', 'dark', time() + 86400 * 30);
// ⚠️ Nouvelle valeur visible seulement au prochain chargement

// Exemple : changement de langue via un lien
if (isset($_GET['lang'])) {
    $lang = $_GET['lang'];
    if (in_array($lang, ['fr', 'en', 'es'])) {
        setcookie('langue', $lang, time() + 86400 * 365);
        header('Location: ' . $_SERVER['PHP_SELF']);
        exit;
    }
}

// SUPPRIMER : timestamp dans le passé (time() - 3600)
setcookie('nom_cookie', '', time() - 3600);

// ⚠️ Utiliser les MÊMES paramètres que lors de la création
// Créé avec path='/'
setcookie('test', 'val', time() + 3600, '/');
// Supprimé avec le même path
setcookie('test', '', time() - 3600, '/');
?>

Exemple : "Se souvenir de moi" 🔑

<?php
session_start();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $email    = $_POST['email'] ?? '';
    $password = $_POST['password'] ?? '';
    $remember = isset($_POST['remember']);

    if ($email === '[email protected]' && $password === '123456') {
        $_SESSION['utilisateur'] = ['email' => $email, 'nom' => 'Marie'];

        if ($remember) {
            // Générer un token aléatoire sécurisé
            $token = bin2hex(random_bytes(32));
            // saveDansLaBDD($email, $token); ← à implémenter
            setcookie('remember_token', $token, [
                'expires'  => time() + 86400 * 30,
                'path'     => '/',
                'secure'   => true,
                'httponly' => true,
                'samesite' => 'Strict'
            ]);
        }
        header('Location: dashboard.php');
        exit;
    }
}
?>
<form method="POST">
    <input type="email" name="email" required>
    <input type="password" name="password" required>
    <label><input type="checkbox" name="remember"> Se souvenir de moi</label>
    <button type="submit">Connexion</button>
</form>
🛡️

Partie 4

Sécurité des Cookies et Sessions

Protection contre les attaques

Les 3 attributs de sécurité 🔐

HttpOnly
🛡️ Empêche JS d'accéder au cookie
Protection XSS
Secure
🔒 Cookie HTTPS uniquement
Protection Man-in-the-Middle
SameSite
🛡️ Contrôle l'envoi cross-site
Protection CSRF

SameSite Strict

Cookie jamais envoyé depuis un autre site → Sessions, authentification

SameSite Lax

Envoyé sur les liens, pas les formulaires → Valeur par défaut recommandée

Cookies sécurisés : la bonne pratique ✅

<?php
// ❌ MAUVAIS : Cookie sans sécurité
setcookie('user_pref', 'dark_mode', time() + 86400 * 30);

// ✅ BON : Cookie sécurisé avec tableau d'options (PHP 7.3+)
setcookie('user_pref', 'dark_mode', [
    'expires'  => time() + 86400 * 30,
    'path'     => '/',
    'secure'   => true,       // HTTPS uniquement
    'httponly' => true,       // Pas de JavaScript
    'samesite' => 'Lax'       // Protection CSRF
]);

// ✅ HELPER RÉUTILISABLE : Fonction helper pour cookies sécurisés
function setCookieSecure(string $name, string $value, int $days = 30): void {
    setcookie($name, $value, [
        'expires'  => time() + (86400 * $days),
        'path'     => '/',
        'secure'   => true,
        'httponly' => true,
        'samesite' => 'Lax'
    ]);
}

setCookieSecure('langue', 'fr', 365);
setCookieSecure('theme', 'dark', 30);
?>

Configuration sécurisée complète 🔐

<?php // config.php — À inclure en tête de chaque page
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
ini_set('session.cookie_samesite', 'Strict');
ini_set('session.use_strict_mode', 1);
ini_set('session.cookie_lifetime', 0);
ini_set('session.gc_maxlifetime', 3600);

session_name('SECURE_SESSION_ID');
session_start();

// Régénérer l'ID toutes les 30 minutes
if (!isset($_SESSION['created'])) {
    $_SESSION['created'] = time();
} elseif (time() - $_SESSION['created'] > 1800) {
    session_regenerate_id(true);
    $_SESSION['created'] = time();
}

// Vérifier IP + User-Agent (anti session hijacking)
if (!isset($_SESSION['ip'])) {
    $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
    $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT'];
} elseif ($_SESSION['ip'] !== $_SERVER['REMOTE_ADDR']) {
    session_destroy();
    die('Session invalide — reconnectez-vous');
}
?>

Protection contre le Session Hijacking 🎭

<?php
session_start();

// 1. Régénérer l'ID après chaque action sensible
if (isset($_POST['login'])) {
    session_regenerate_id(true); // Nouveau ID, supprime l'ancien
}

// 2. Timeout de session (30 min d'inactivité)
$timeout = 1800;
if (isset($_SESSION['last_activity'])) {
    if (time() - $_SESSION['last_activity'] > $timeout) {
        session_destroy();
        header('Location: login.php?timeout=1');
        exit;
    }
}
$_SESSION['last_activity'] = time();

// 3. Token CSRF dans la session
if (!isset($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// 4. Vérifier le token CSRF sur les formulaires POST
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $token = $_POST['csrf_token'] ?? '';
    if (!hash_equals($_SESSION['csrf_token'], $token)) {
        die('CSRF token invalide');
    }
}
?>
⚖️

Partie 5

RGPD & CNIL : Obligations légales

Culture juridique des cookies

Qu'est-ce que le RGPD ? 📜

RGPD = Règlement Général sur la Protection des Données — Loi européenne en vigueur depuis le 25 mai 2018. La CNIL est l'autorité française chargée de son application.

Principes clés

  • 🔒 Protection des données personnelles
  • 👤 Droits des utilisateurs renforcés
  • ✅ Consentement explicite obligatoire
  • 📋 Transparence sur l'usage

Amendes

  • 💰 Jusqu'à 20 millions €
  • 💰 Ou 4% du CA annuel
  • 🔴 Google : 90M€ (2020)
  • 🔴 Amazon : 746M€ (2021)

Les 3 types de cookies 🍪

TypeConsentementDescriptionExemples
Essentiels❌ Non requisNécessaires au fonctionnementSession, panier, sécurité
Fonctionnels✅ RequisAméliore l'expérienceLangue, préférences
Analytics / Pub✅ RequisTracking, publicitéGoogle Analytics, FB Pixel

Obligations ✅

  • Consentement AVANT le dépôt
  • Bouton Refuser accessible
  • Politique de confidentialité

Interdit ❌

  • Cases pré-cochées
  • "Continuer = accepter"
  • Bloquer l'accès au refus

Bandeau de consentement RGPD ✅

<?php
session_start();
$consentement = $_COOKIE['cookie_consent'] ?? null;
?>
<?php if ($consentement === null): ?>
<div id="cookie-banner"
     style="position:fixed;bottom:0;width:100%;background:#222;color:#fff;padding:15px;text-align:center;">
    <p>🍪 Ce site utilise des cookies.
       <a href="politique.php" style="color:#f0ad4e;">En savoir plus</a></p>
    <button onclick="acceptCookies()">Accepter</button>
    <button onclick="refuseCookies()">Refuser</button>
    <button onclick="location.href='preferences.php'">Personnaliser</button>
</div>
<script>
function acceptCookies() {
    document.cookie = "cookie_consent=all; max-age=31536000; path=/; secure; samesite=lax";
    document.getElementById('cookie-banner').style.display = 'none';
}
function refuseCookies() {
    document.cookie = "cookie_consent=essential; max-age=31536000; path=/; secure; samesite=lax";
    document.getElementById('cookie-banner').style.display = 'none';
}
</script>
<?php endif; ?>
🔑

Partie 6

Système d'authentification complet

Login / Logout sécurisé

Architecture d'authentification 🏗️

auth/
├── config.php          → Configuration sécurité + PDO
├── login.php           → Formulaire de connexion
├── logout.php          → Déconnexion
├── register.php        → Inscription (optionnel)
├── dashboard.php       → Page protégée
└── auth_functions.php  → Fonctions réutilisables
1. Utilisateur → login.php
2. Vérification identifiants en BDD
→ Échec : afficher message d'erreur
→ Succès : créer session + redirection
3. dashboard.php → vérifier session
→ Pas de session : redirection login
→ Session valide : afficher le dashboard

Fonctions d'authentification 🔐

<?php // auth_functions.php
function isLoggedIn(): bool {
    return isset($_SESSION['user_id']);
}

function requireLogin(): void {
    if (!isLoggedIn()) {
        header('Location: login.php');
        exit;
    }
}

function logout(): void {
    $_SESSION = [];
    if (isset($_COOKIE['AUTH_SESSION'])) {
        setcookie('AUTH_SESSION', '', time() - 3600, '/');
    }
    session_destroy();
}

function hashPassword(string $password): string {
    return password_hash($password, PASSWORD_DEFAULT);
}

function verifyPassword(string $password, string $hash): bool {
    return password_verify($password, $hash);
}
?>

Page de connexion complète 🔑

<?php // login.php
require 'config.php';
require 'auth_functions.php';

if (isLoggedIn()) { header('Location: dashboard.php'); exit; }

$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $email    = trim($_POST['email'] ?? '');
    $password = $_POST['password'] ?? '';

    if (empty($email) || empty($password)) {
        $error = "Tous les champs sont obligatoires";
    } else {
        $stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
        $stmt->execute([$email]);
        $user = $stmt->fetch();

        if ($user && verifyPassword($password, $user['password'])) {
            session_regenerate_id(true); // ← OBLIGATOIRE après login
            $_SESSION['user_id']    = $user['id'];
            $_SESSION['user_email'] = $user['email'];
            $_SESSION['user_name']  = $user['name'];
            $_SESSION['user_role']  = $user['role'];
            header('Location: dashboard.php');
            exit;
        } else {
            $error = "Email ou mot de passe incorrect";
        }
    }
}
?>

Page protégée + Déconnexion 🛡️

<?php // dashboard.php
require 'config.php';
require 'auth_functions.php';

requireLogin(); // Redirige si non connecté

$userName = $_SESSION['user_name'];
$userRole = $_SESSION['user_role'];
?>
<h1>Bienvenue, <?= htmlspecialchars($userName) ?> !</h1>
<?php if ($userRole === 'admin'): ?>
    <h2>Administration</h2>
<?php endif; ?>
<a href="logout.php">Se déconnecter</a>
<?php // logout.php
require 'config.php';
require 'auth_functions.php';

if (isLoggedIn()) {
    // Supprimer le remember_token en BDD
    $stmt = $pdo->prepare("UPDATE users SET remember_token=NULL WHERE id=?");
    $stmt->execute([$_SESSION['user_id']]);
}

if (isset($_COOKIE['remember_token'])) {
    setcookie('remember_token', '', time() - 3600, '/');
}

logout();
header('Location: login.php?logout=1');
exit;
?>

Checklist sécurité authentification ✅

  • Mots de passe hashés avec password_hash()
  • HttpOnly sur cookies sensibles
  • Secure (HTTPS) activé
  • SameSite Strict sur session
  • session_regenerate_id() après login
  • Tokens aléatoires pour "Se souvenir"
  • Timeout de session (30 min)
  • Protection CSRF avec tokens
  • Limiter les tentatives de connexion
  • Logs des connexions suspectes
  • HTTPS obligatoire en production
  • htmlspecialchars() à l'affichage
💪

Partie 7

Exercices pratiques

Mettre en pratique

Exercice 1 : Préférence de langue 🌍

📝 Énoncé : Créez langue.php qui :
• Affiche "Bonjour" / "Hello" / "Hola" selon la langue
• 3 liens pour changer la langue (fr, en, es)
• Stocke le choix dans un cookie (30 jours)
• Langue par défaut : français
💡 Superglobales utilisées : $_GET + $_COOKIE + setcookie()

Exercice 1 : Corrigé ✅

<?php
$traductions = [
    'fr' => ['bonjour' => 'Bonjour', 'bienvenue' => 'Bienvenue sur notre site'],
    'en' => ['bonjour' => 'Hello',   'bienvenue' => 'Welcome to our website'],
    'es' => ['bonjour' => 'Hola',    'bienvenue' => 'Bienvenido a nuestro sitio']
];

if (isset($_GET['lang'])) {
    $lang = $_GET['lang'];
    if (in_array($lang, ['fr', 'en', 'es'])) {
        setcookie('langue', $lang, time() + 86400 * 30, '/');
        header('Location: langue.php');
        exit;
    }
}

$langue = $_COOKIE['langue'] ?? 'fr';
$textes = $traductions[$langue];
?>
<h1><?= $textes['bonjour'] ?> !</h1>
<p><?= $textes['bienvenue'] ?></p>
<a href="?lang=fr">🇫🇷 Français</a> |
<a href="?lang=en">🇬🇧 English</a> |
<a href="?lang=es">🇪🇸 Español</a>

Exercice 2 : Compteur de visites 📊

📝 Énoncé : Créez compteur.php qui :
• Affiche "C'est votre visite n°X"
• Incrémente le compteur à chaque visite
• Utilise un cookie (7 jours)
• "Première visite" si pas de cookie
• Bouton "Réinitialiser" qui supprime le cookie
💡 Superglobales utilisées : $_COOKIE + $_GET + setcookie()

Exercice 2 : Corrigé ✅

<?php
if (isset($_GET['reset'])) {
    setcookie('compteur_visites', '', time() - 3600, '/');
    header('Location: compteur.php');
    exit;
}

$visites = (int)($_COOKIE['compteur_visites'] ?? 0) + 1;
setcookie('compteur_visites', $visites, time() + 86400 * 7, '/');
?>
<h1>Compteur de visites</h1>
<?php if ($visites === 1): ?>
    <p>🎉 Bienvenue ! C'est votre première visite.</p>
<?php else: ?>
    <p>C'est votre visite n°<strong><?= $visites ?></strong></p>
<?php endif; ?>
<a href="?reset=1">Réinitialiser le compteur</a>

Exercice 3 : Panier de shopping 🛒

📝 Énoncé : Créez panier.php qui :
• Affiche 3 produits (nom + prix)
• Bouton "Ajouter au panier" pour chaque produit
• Stocke le panier en SESSION
• Affiche le contenu du panier + total
• Bouton "Vider le panier"
💡 Superglobales utilisées : $_SESSION + $_GET + session_start()

Exercice 3 : Corrigé (1/2) ✅

<?php
session_start();
$produits = [
    1 => ['nom' => 'Ordinateur', 'prix' => 899],
    2 => ['nom' => 'Souris', 'prix' => 25],
    3 => ['nom' => 'Clavier', 'prix' => 45]
];
if (!isset($_SESSION['panier'])) $_SESSION['panier'] = [];

if (isset($_GET['add'])) {
    $id = (int)$_GET['add'];
    if (isset($produits[$id])) {
        if (isset($_SESSION['panier'][$id])) {
            $_SESSION['panier'][$id]['quantite']++;
        } else {
            $_SESSION['panier'][$id] = [
                'nom' => $produits[$id]['nom'],
                'prix' => $produits[$id]['prix'],
                'quantite' => 1
            ];
        }
    }
    header('Location: panier.php'); exit;
}
if (isset($_GET['clear'])) {
    $_SESSION['panier'] = [];
    header('Location: panier.php'); exit;
}
$total = array_sum(array_map(
    fn($i) => $i['prix'] * $i['quantite'],
    $_SESSION['panier']
));
?>

Exercice 3 : Corrigé (2/2) ✅

<h1>Boutique</h1>
<h2>Produits disponibles</h2>
<?php foreach ($produits as $id => $p): ?>
    <div>
        <h3><?= $p['nom'] ?></h3>
        <p><?= $p['prix'] ?> €</p>
        <a href="?add=<?= $id ?>">Ajouter au panier</a>
    </div>
<?php endforeach; ?>

<h2>Mon panier (<?= count($_SESSION['panier']) ?> article(s))</h2>
<?php if (empty($_SESSION['panier'])): ?>
    <p>Votre panier est vide.</p>
<?php else: ?>
    <table border="1">
        <tr><th>Produit</th><th>Prix</th><th>Qté</th><th>Sous-total</th></tr>
        <?php foreach ($_SESSION['panier'] as $item): ?>
        <tr>
            <td><?= $item['nom'] ?></td>
            <td><?= $item['prix'] ?> €</td>
            <td><?= $item['quantite'] ?></td>
            <td><?= $item['prix'] * $item['quantite'] ?> €</td>
        </tr>
        <?php endforeach; ?>
        <tr><td colspan="3"><strong>TOTAL</strong></td><td><?= $total ?> €</td></tr>
    </table>
    <a href="?clear=1">Vider le panier</a>
<?php endif; ?>

🎯 Récapitulatif

  • ✅ Superglobales : définition, 9 variables, sécurité
  • ✅ $_SESSION : tableau serveur, session_start()
  • ✅ $_COOKIE : tableau navigateur, setcookie()
  • ✅ Sécurité : HttpOnly, Secure, SameSite
  • ✅ RGPD : consentement, types de cookies
  • ✅ Authentification : login, logout, remember me
  • ✅ Protection : hijacking, CSRF, XSS
  • ✅ Exercices : langue, compteur, panier

Points clés à retenir 📝

🔷 Superglobales

  • Tableaux accessibles partout
  • Toujours préfixées par $_
  • Créées et gérées par PHP
  • isset() avant de lire
  • htmlspecialchars() avant d'afficher

🍪 Sessions & Cookies

  • Cookie = Navigateur, Session = Serveur
  • session_start() AVANT tout HTML
  • setcookie() AVANT tout HTML
  • HttpOnly + Secure + SameSite
  • RGPD : consentement obligatoire

Ressources utiles 📚

  • 📖 php.net → Documentation officielle sessions/cookies/superglobales
  • ⚖️ cnil.fr → Guide RGPD cookies
  • 🔐 OWASP → Sécurité web
  • 🛡️ Mozilla MDN → HTTP cookies
  • 📘 PHP The Right Way → Best practices

Merci ! 👏

Des questions ?

🔷 Superglobales · 🍪 Cookies · 📦 Sessions

= La mémoire du Web !