Audit de Sécurité - Playlab42#
Date: 2025-12-14 Auditeur: Claude Code Périmètre: Frontend JavaScript/TypeScript, dépendances npm, workflows CI/CD
🎯 Résumé Exécutif#
Ce projet étant un support de formation, la sécurité est cruciale pour protéger les apprenants et leurs données. L'audit a identifié 7 catégories de vulnérabilités nécessitant une attention, dont 3 critiques.
Statistiques#
- Vulnérabilités critiques: 3
- Vulnérabilités moyennes: 3
- Vulnérabilités mineures: 1
- Bonnes pratiques: 4 ✓
🔴 Vulnérabilités Critiques#
1. PostMessage sans validation d'origine#
Sévérité: 🔴 CRITIQUE CWE: CWE-346 (Origin Validation Error)
Description#
Les communications via postMessage n'utilisent pas de validation d'origine, permettant à n'importe quel site malveillant d'envoyer des messages au portail.
Localisation#
lib/gamekit.js:110-window.parent.postMessage(message, '*')app.js:1271-1288- Aucune validation deevent.origin
Impact#
- Un site malveillant pourrait envoyer des commandes au portail
- Injection de faux scores
- Manipulation de l'état de l'application
- Vol de données localStorage
Code vulnérable#
// lib/gamekit.js:110
_postMessage(message) {
if (window.parent !== window) {
window.parent.postMessage(message, '*'); // ⚠️ Accepte toutes les origines
}
}
// app.js:1271
on(window, 'message', (e) => {
if (!e.data || !e.data.type) {return;} // ⚠️ Pas de vérification de e.origin
// ...
});
Recommandations#
// gamekit.js - Utiliser l'origine spécifique
_postMessage(message) {
if (window.parent !== window) {
const allowedOrigin = window.location.origin; // ou une liste d'origines autorisées
window.parent.postMessage(message, allowedOrigin);
}
}
// app.js - Valider l'origine
on(window, 'message', (e) => {
// Valider l'origine
const allowedOrigins = [
window.location.origin,
'https://z4ppy.github.io', // GitHub Pages
// Ajouter d'autres origines de confiance
];
if (!allowedOrigins.includes(e.origin)) {
console.warn('[Security] Message from untrusted origin:', e.origin);
return;
}
if (!e.data || !e.data.type) {return;}
// ...
});
2. Content Security Policy (CSP) manquante#
Sévérité: 🔴 CRITIQUE CWE: CWE-1021 (Improper Restriction of Rendered UI Layers)
Description#
Aucune politique CSP n'est définie, ce qui permet l'exécution de scripts inline et le chargement de ressources depuis n'importe quelle origine.
Localisation#
index.html- Aucun header<meta http-equiv="Content-Security-Policy">
Impact#
- Vulnérabilité XSS (Cross-Site Scripting)
- Injection de scripts malveillants
- Clickjacking
- Chargement de ressources depuis des CDN compromis
Recommandations#
Ajouter dans index.html (après <meta charset="UTF-8">) :
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self';
frame-src 'self';
base-uri 'self';
form-action 'self';
">
Note: Si GitHub Pages nécessite des ajustements, adapter la politique en conséquence.
3. Sandbox iframe insuffisamment restrictif#
Sévérité: 🔴 CRITIQUE CWE: CWE-829 (Inclusion of Functionality from Untrusted Control Sphere)
Description#
L'iframe utilisée pour charger les jeux/outils a un sandbox avec allow-same-origin, ce qui permet aux iframes d'accéder au localStorage et aux cookies du parent.
Localisation#
index.html:95-<iframe id="game-iframe" sandbox="allow-scripts allow-same-origin">
Impact#
- Un jeu malveillant pourrait accéder au localStorage du portail
- Vol de scores, préférences, progression
- Modification de données sensibles
Code vulnérable#
<iframe id="game-iframe" sandbox="allow-scripts allow-same-origin"></iframe>
Recommandations#
<!-- Option 1 : Sandbox strict (préféré si les jeux n'ont pas besoin de localStorage partagé) -->
<iframe id="game-iframe" sandbox="allow-scripts"></iframe>
<!-- Option 2 : Si localStorage partagé nécessaire, utiliser une origin différente -->
<!-- Héberger les jeux sur un sous-domaine différent (ex: games.playlab42.com) -->
Note importante: Retirer allow-same-origin empêchera les jeux d'accéder au localStorage du portail. Si cette communication est nécessaire, utiliser uniquement postMessage avec validation d'origine.
🟠 Vulnérabilités Moyennes#
4. Validation insuffisante des URLs dans les bookmarks#
Sévérité: 🟠 MOYENNE CWE: CWE-79 (XSS)
Description#
Les URLs des bookmarks sont validées mais n'empêchent pas les protocoles dangereux comme javascript: ou data:.
Localisation#
scripts/build-bookmarks.js:68-74- Validation basique avecnew URL()
Impact#
- Possibilité d'injecter des URLs
javascript:qui exécutent du code - XSS via
data:text/html,...
Code vulnérable#
function validateBookmark(bookmark, source) {
// ...
try {
new URL(bookmark.url); // ⚠️ Accepte javascript: et data:
} catch {
stats.errors.push(`URL invalide: ${bookmark.url} (${source})`);
return false;
}
return true;
}
Recommandations#
function validateBookmark(bookmark, source) {
if (!bookmark.url) {
stats.errors.push(`Bookmark sans URL (${source})`);
return false;
}
if (!bookmark.title) {
stats.errors.push(`Bookmark sans titre: ${bookmark.url} (${source})`);
return false;
}
// Valider le protocole
try {
const url = new URL(bookmark.url);
const allowedProtocols = ['http:', 'https:'];
if (!allowedProtocols.includes(url.protocol)) {
stats.errors.push(`Protocole non autorisé (${url.protocol}): ${bookmark.url} (${source})`);
return false;
}
} catch {
stats.errors.push(`URL invalide: ${bookmark.url} (${source})`);
return false;
}
return true;
}
5. Pas de vérification d'intégrité des dépendances#
Sévérité: 🟠 MOYENNE CWE: CWE-494 (Download of Code Without Integrity Check)
Description#
Les dépendances npm ne sont pas vérifiées avec SRI (Subresource Integrity) et les versions ne sont pas pinnées strictement.
Localisation#
package.json- Utilisation de^pour les versions
Impact#
- Attaque supply chain via des versions compromises
- Installation de packages malveillants
Recommandations#
- Utiliser
package-lock.jsonen production (déjà présent ✓) - Vérifier régulièrement avec
npm audit - Considérer l'utilisation de
npm ciau lieu denpm installen CI (déjà fait ✓)
6. LocalStorage sans validation stricte#
Sévérité: 🟠 MOYENNE CWE: CWE-502 (Deserialization of Untrusted Data)
Description#
Plusieurs endroits utilisent JSON.parse() sur des données localStorage sans validation approfondie du contenu.
Localisation#
app.js:113,119,125,128,387-JSON.parse()avec try-catch minimallib/gamekit.js:154,177,211,248- Idem
Impact#
- Injection de données malveillantes dans localStorage (via DevTools ou XSS)
- Corruption de l'état de l'application
- Potentiel DoS (denial of service) si données malformées
Recommandations#
// Ajouter une validation de schéma après JSON.parse
function loadPreferences() {
try {
const player = localStorage.getItem(STORAGE_KEYS.PLAYER);
if (player) {
const parsed = JSON.parse(player);
// Valider le schéma
if (typeof parsed !== 'object' || parsed === null) {
throw new Error('Invalid player data');
}
if (parsed.name && typeof parsed.name !== 'string') {
throw new Error('Invalid player name');
}
state.preferences.pseudo = parsed.name || 'Anonyme';
}
// ... suite
} catch (e) {
console.warn('Erreur chargement préférences, réinitialisation:', e);
// Nettoyer les données corrompues
localStorage.removeItem(STORAGE_KEYS.PLAYER);
}
}
🟡 Vulnérabilités Mineures#
7. Pas de rate limiting sur les requêtes externes#
Sévérité: 🟡 MINEURE CWE: CWE-770 (Allocation of Resources Without Limits)
Description#
Le script build-bookmarks.js fait des requêtes pour récupérer les métadonnées OG sans rate limiting strict.
Localisation#
scripts/build-bookmarks.js:296-315- Batch de 5 requêtes concurrentes
Impact#
- Possibilité de se faire bloquer par les serveurs cibles
- Potentiel DoS involontaire
Recommandations#
- Ajouter un délai entre les batches
- Implémenter un retry avec backoff exponentiel
- Respecter les headers
Retry-After
✅ Bonnes Pratiques Détectées#
- Utilisation de
escapeHtml()- Protection XSS danslib/dom.js:94 - Sandbox iframe - Utilisation de l'attribut
sandbox(même s'il peut être amélioré) - Try-catch sur JSON.parse - Protection contre les données corrompues
- Validation des manifests - Vérification des champs requis dans
build-catalogue.js
📋 Plan d'Action Recommandé#
Priorité 1 (URGENT)#
- Ajouter validation d'origine pour
postMessage - Implémenter une CSP stricte
- Restreindre le sandbox des iframes
Priorité 2 (IMPORTANT)#
- Valider les protocoles des URLs (whitelist http/https)
- Ajouter validation de schéma pour localStorage
- Configurer Dependabot pour les mises à jour de sécurité
Priorité 3 (AMÉLIORATION)#
- Ajouter rate limiting pour les requêtes OG
- Implémenter des tests de sécurité automatisés
- Créer un workflow GitHub Actions pour l'audit de sécurité
🤖 Outils d'Automatisation Recommandés#
1. npm audit#
npm audit --audit-level=moderate
Vérifie les vulnérabilités dans les dépendances npm.
2. ESLint avec plugins de sécurité#
npm install --save-dev eslint-plugin-security eslint-plugin-no-unsanitized
3. GitHub Dependabot#
Activer dans .github/dependabot.yml pour des mises à jour automatiques.
4. CodeQL#
Activer dans GitHub Actions pour l'analyse statique de code.
5. OWASP Dependency-Check#
Scanner de vulnérabilités CVE pour les dépendances.
6. Snyk#
Scanner de sécurité spécialisé JavaScript/npm.
📚 Références#
- OWASP Top 10 2021
- MDN - Content Security Policy
- MDN - postMessage Security
- HTML5 Security Cheatsheet
- npm Security Best Practices
Fin du rapport d'audit