Skip to Content

Terrain 16 — Sécurité & qualité

⏱️ TL;DR — 20 mini-jeux ⭐⭐⭐⭐ sur les réflexes de sécurité et de qualité d’un plugin Moodle : require_login, capabilities, sesskey, PARAM_*, DML paramétré, échappement de sortie, Privacy API, coding style, tests. Ce sont les règles que la review moodle.org traque — et qui séparent un plugin pro d’une passoire.

⚠️ La devise : « Vérifier l’accès (login + capability). Assainir l’entrée (PARAM). Échapper la sortie (s/format). Protéger les actions (sesskey). »


🕹️ Mini-jeu 1 — require_login ⭐⭐⭐⭐ 💻

But : exiger une session. Mission : protège une page de plugin. Étapes :

require_login($course, true, $cm);

✅ Solution

require_login() garantit un utilisateur connecté et l’accès au cours/activité (visibilité, inscription, restrictions). Première ligne de toute page. Sans elle, l’accès est ouvert. Variantes : require_login() seul (contexte système).


🕹️ Mini-jeu 2 — La capability ⭐⭐⭐⭐ 💻

But : l’autorisation fine. Mission : exige une capability dans le bon contexte. Étapes :

require_capability('mod/simplecheck:grade', $context);

✅ Solution

require_capability($cap, $context) bloque si le droit manque dans ce contexte (terrain 11). require_login dit « connecté » ; require_capability dit « autorisé ». Les deux sont nécessaires. has_capability() renvoie un booléen pour un affichage conditionnel.


🕹️ Mini-jeu 3 — Le sesskey ⭐⭐⭐⭐ 💻

But : anti-CSRF. Mission : protège une action destructive. Étapes :

require_sesskey(); // ou confirm_sesskey()

✅ Solution

Le sesskey prouve que la requête vient de ton interface (pas d’un site tiers = CSRF). Toute action qui modifie un état via GET/POST hors Form API exige require_sesskey(), et le lien doit porter sesskey(). La Form API le gère automatiquement.


🕹️ Mini-jeu 4 — required_param + PARAM_* ⭐⭐⭐⭐ 💻

But : lire l’entrée en sécurité. Mission : lis un id et une action. Étapes :

$id = required_param('id', PARAM_INT); $act = optional_param('action', 'view', PARAM_ALPHA);

✅ Solution

required_param/optional_param + un type PARAM (INT, ALPHA, TEXT, ALPHANUMEXT, RAW…) lisent et assainissent. On ne touche jamais $_GET/$_POST bruts. Le type est la première défense. RAW = non filtré (à éviter/échapper à la sortie).


🕹️ Mini-jeu 5 — DML paramétré ⭐⭐⭐⭐⭐ 💻

But : anti-injection SQL. Mission : interroge la base avec un paramètre utilisateur. Étapes :

$record = $DB->get_record('simplecheck', ['id' => $id, 'course' => $courseid], '*', MUST_EXIST);

✅ Solution

Le DML paramètre les conditions → pas d’injection. Pour du SQL custom : $DB->get_records_sql($sql, $params) avec des placeholders (:id), jamais de concaténation. Privilégie l’API haut niveau (get_record(s), get_in_or_equal).


🕹️ Mini-jeu 6 — Échapper le texte simple ⭐⭐⭐⭐ 💻

But : anti-XSS à la sortie. Mission : affiche une donnée utilisateur en sécurité. Étapes :

echo s($user->firstname); // texte brut échappé echo format_string($course->fullname); // noms (filtres appliqués)

✅ Solution

s() échappe un texte pour du HTML ; format_string() échappe et applique les filtres (pour les noms/titres). On échappe au moment de la sortie. Les templates Mustache échappent déjà {{var}} automatiquement (préfère-les).


🕹️ Mini-jeu 7 — Formater du contenu riche ⭐⭐⭐⭐ 💻

But : afficher du HTML sûr. Mission : affiche une description (HTML) en sécurité. Étapes :

echo format_text($instance->intro, $instance->introformat, ['context' => $context]);

✅ Solution

format_text() nettoie le HTML (anti-XSS), applique filtres et gère les fichiers embarqués (via le contexte). Pour du contenu riche (intro, forum). ⚠️ Ne jamais afficher du HTML utilisateur sans format_text (ou s() pour du texte simple).


🕹️ Mini-jeu 8 — clean_param ⭐⭐⭐ 💻

But : assainir une valeur en code. Mission : assainis une valeur reçue autrement que par param. Étapes :

$slug = clean_param($raw, PARAM_ALPHANUMEXT);

✅ Solution

clean_param($value, PARAM_*) applique le filtrage d’un type PARAM à une valeur déjà en main (issue d’un calcul, d’un webservice). Complète required_param. Toujours typer les données douteuses.


🕹️ Mini-jeu 9 — Démo XSS ⭐⭐⭐⭐ 💻

But : voir la faille pour la comprendre. Mission : affiche une saisie sans échapper (en local), constate, puis corrige avec s().

✅ Solution

echo $data->name avec <script>alert(1)</script> exécute le script (XSS). Correction : echo s($data->name). Fais-le en local : voir la faille tirer ancre le réflexe d’échappement bien mieux que la théorie.


🕹️ Mini-jeu 10 — Démo CSRF ⭐⭐⭐⭐ 💻

But : comprendre le sesskey par l’absence. Mission : une action de suppression sans sesskey — explique l’attaque.

✅ Solution

Sans sesskey, un site tiers peut déclencher une suppression au nom d’un enseignant connecté (CSRF). require_sesskey() casse l’attaque (l’attaquant ne peut pas deviner le jeton). D’où : jamais d’action d’état sans sesskey (ou via Form API).


🕹️ Mini-jeu 11 — Sécurité de la File API ⭐⭐⭐⭐⭐ 💻

But : servir des fichiers en sécurité. Mission : repère comment un plugin sert un fichier (contexte + capability).

✅ Solution

Servir un fichier passe par un callback *_pluginfile() qui vérifie login + capability + contexte avant de délivrer le fichier (via send_stored_file). Sinon, fuite de documents privés. La File API sécurise l’accès aux fichiers stockés (Partie 5).


🕹️ Mini-jeu 12 — Le Privacy API ⭐⭐⭐⭐⭐ 💻

But : conformité RGPD (obligatoire). Mission : implémente le provider (metadata + export + delete).

✅ Solution

Tout plugin stockant des données perso doit un classes/privacy/provider.php : déclarer les données (get_metadata), les exporter (export_user_data) et les supprimer (delete_data_for_user). Sans lui, install signalée non conforme → refus wp… pardon, moodle.org (Partie 11).


🕹️ Mini-jeu 13 — Zéro chaîne en dur ⭐⭐⭐ 💻

But : i18n = qualité. Mission : remplace un texte en dur par get_string. Étapes :

echo get_string('mytasks', 'local_todolist');

✅ Solution

Aucune chaîne visible en dur : tout via get_string() + lang/en/. C’est de l’i18n ET une exigence de qualité (le codechecker le vérifie). Un texte non traduisible = un plugin non publiable. (Côté JS : core/str, terrain 14.)


🕹️ Mini-jeu 14 — L’en-tête GPL & PHPDoc ⭐⭐⭐ 💻

But : conformité de forme. Mission : ajoute l’en-tête GPL et un @package en tête de fichier.

✅ Solution

Chaque fichier PHP porte l’en-tête GPL standard + PHPDoc (@package, @copyright, @license). Le générateur de squelette (terrain 12) les met. Exigé par le coding style et la review. Forme = signal de sérieux.


🕹️ Mini-jeu 15 — Le coding style ⭐⭐⭐⭐ 💻

But : passer le linter. Mission : lance moodle-cs et corrige les écarts.

✅ Solution

moodle-cs (PHP_CodeSniffer) vérifie indentation, nommage, PHPDoc, fonctions interdites, patterns de sécurité. Vise zéro erreur. À lancer en continu (VS Code/CI), pas à la fin. C’est le juge de forme de moodle.org.


🕹️ Mini-jeu 16 — Un test PHPUnit ⭐⭐⭐⭐⭐ 💻

But : prouver que ça marche. Mission : écris un test de ..._add_instance dans tests/.

✅ Solution

Un test étend advanced_testcase, utilise $this->getDataGenerator() pour créer cours/users/instances, et assert le comportement. $this->resetAfterTest(). Lancé avec vendor/bin/phpunit. Filet contre les régressions (terrain 12, jeu 20).


🕹️ Mini-jeu 17 — Un scénario Behat ⭐⭐⭐⭐⭐ 💻

But : tester le parcours utilisateur. Mission : écris un scénario tests/behat/ (créer et cocher une checklist).

✅ Solution

Behat décrit le parcours en langage Given/When/Then (Gherkin), joué dans un navigateur. Vérifie l’UX de bout en bout (l’étudiant coche → la note apparaît). Lourd mais précieux pour un mod. Lancé via l’environnement Behat (terrain 12).


🕹️ Mini-jeu 18 — Frankenstyle sans collision ⭐⭐⭐ 💻

But : éviter les conflits. Mission : vérifie que toutes tes fonctions/tables/strings sont préfixées.

✅ Solution

Le frankenstyle (local_todolist_, mod_simplecheck_) évite les collisions avec le cœur et les autres plugins. Fonctions globales, tables, options, capabilities, événements : tout préfixé. Une fonction save() non préfixée casserait potentiellement le site.


🕹️ Mini-jeu 19 — Ne jamais hacker le cœur ⭐⭐⭐ 💻

But : la règle absolue. Mission : un besoin semble impossible sans modifier le cœur — que fais-tu ?

✅ Solution

Jamais de core hack (chaque MAJ l’écraserait, et c’est un risque de sécurité). On passe par hooks/callbacks, un plugin du bon type, ou une surcharge propre. Si vraiment impossible, on propose un patch au cœur (Partie 11). Tout est faisable par plugin.


🕹️ Mini-jeu 20 — Check-list sécurité/qualité ⭐⭐⭐⭐ 💻

But : capstone du terrain. Mission : audite local_todolist/mod_simplecheck : login + capability sur chaque page, sesskey sur chaque action, PARAM sur chaque entrée, DML paramétré, sortie échappée, privacy provider, i18n complète, coding style vert, quelques tests.

✅ Solution

Si tu coches tout, tes plugins sont sûrs et publiables. Ces réflexes sont exactement ce que traque la review moodle.org (Partie 11). La sécurité/qualité n’est pas une étape finale : c’est une façon d’écrire chaque ligne. Cap sur l’ouverture vers l’extérieur : les web services (terrain 17).


Terrain suivant : 17 — Web services.