La sécurité et la confidentialité des utilisateurs de CinéCloud FR sont des priorités absolues. Ce document décrit notre politique de sécurité, les mesures architecturales mises en œuvre, et la procédure de signalement responsable de vulnérabilités.
Nous fournissons activement des correctifs de sécurité pour les versions suivantes :
| Version | Statut |
|---|---|
| 2.4.x (Actuelle) | ✅ Support actif (mises à jour de sécurité et correctifs) |
| 2.x | ✅ Support des correctifs critiques |
| < 2.0 | ❌ Fin de vie (non supporté) |
Nous encourageons l'ensemble des administrateurs d'instances à déployer la dernière version disponible (ghcr.io/d4rk5/cinecloud:latest).
CinéCloud FR est conçu selon le principe de défense en profondeur (defense-in-depth) :
- Les clés API tierces (AllDebrid, Torbox, Prowlarr) fournies par les utilisateurs ne sont jamais stockées en clair dans la base SQLite (
nuvio.db). - Elles sont systématiquement chiffrées avec AES-256-GCM avec vecteur d'initialisation unique (IV) et tag d'authentification cryptographique généré via l'API native
node:crypto. - La clé de chiffrement principale (
APP_SECRET) est soit injectée par l'administrateur, soit auto-générée au premier démarrage dansdata/.app_secretavec un caractère aléatoire cryptographique fort (32 octets).
- Les fichiers contenant des secrets critiques (
data/.app_secretetdata/.admin_password) sont protégés avec des permissions strictes :- POSIX (Linux, Docker, macOS) : permissions
0600(-rw-------), accessibles uniquement par l'utilisateur du processus (non-rootnodeen conteneur Docker). - Windows : permissions restreintes à l'utilisateur courant via
icaclsavec suppression de l'héritage d'accès.
- POSIX (Linux, Docker, macOS) : permissions
- Les variables d'environnement sont validées au démarrage (fail-fast) via
lib/env.jsafin d'empêcher tout démarrage avec des paramètres corrompus ou des secrets par défaut vulnérables. - L'ensemble des routes d'administration (
/api/admin/*) appliquent des schémas de validation stricts via Zod (lib/admin-schemas.js), prévenant les injections, les pollutions de paramètres et les données malformées.
- Les comparaisons de mots de passe administrateur et de hachages utilisateurs sont réalisées via
crypto.timingSafeEqualpour empêcher l'analyse différentielle de temps de réponse. - Le hachage des mots de passe utilisateurs utilise l'algorithme standard scrypt avec sel cryptographique aléatoire de 16 octets (
crypto.scryptSync).
- Les routes publiques Stremio (
/manifest.json,/catalog/*,/stream/*) autorisent les clients Stremio Web et applications tierces. - Les routes sensibles (
/api/*et/api/admin/*) appliquent un contrôle d'origine strict (Origin/Referer) pour bloquer les requêtes de sites web tiers malveillants (Cross-Site Request Forgery / CSRF).
- Des limiteurs de débit dédiés (
express-rate-limit) protègent les surfaces sensibles contre les attaques par force brute et par déni de service (DoS) :- Connexion administrateur (
authLimiter: 30 tentatives / 15 min) - Sondes et vérifications de clés (
apiCheckLimiter: 30 requêtes / min) - Résolution de flux (
resolveLimiter: 60 requêtes / min) - Maintenance & actions admin (
adminActionLimiter: 30 requêtes / min)
- Connexion administrateur (
- Un garde-fou réseau dédié (
lib/net-guard.js) protège les requêtes sortantes déclenchées par l'utilisateur. Il autorise les adresses privées/loopback (indispensables aux services auto-hébergés :prowlarr:9696,host.docker.internal, LAN,127.0.0.1) mais bloque les cibles dangereuses : link-local et métadonnées cloud (169.254.0.0/16, dont169.254.169.254), adresses non spécifiées, multicast et réservées — y compris après résolution DNS (anti DNS-rebinding). Il est appliqué aux sondes/api/check/prowlarret/api/check/lumio, ainsi qu'à la validation des URL Prowlarr partagées. - Pour les instances exposées publiquement (tunnel/Internet) où le test de connectivité
Prowlarr n'est pas nécessaire à des tiers, il est recommandé de restreindre l'accès aux
endpoints
/api/check/*(reverse proxy, authentification amont) : ils permettent par nature de faire sonder un service interne à l'instance. - La confiance accordée aux en-têtes
X-Forwarded-*est désactivée par défaut (TRUST_PROXY=false) :req.ipcorrespond alors à l'adresse socket réelle, non falsifiable. Elle ne doit être activée (TRUST_PROXY=1) que derrière un reverse proxy / tunnel de confiance.
- Principe général : les clés API sont transmises en en-tête (
Authorization: Bearer …) et jamais dans les URLs, afin d'éviter leur fuite via les journaux, proxys et historiques. - Exception documentée : l'endpoint
GET /v1/api/torrents/requestdlde Torbox exige le token en paramètre de requête (?token=…). Un appel avec le seul en-têteAuthorizationéchoue en HTTP 422 ({"detail":[{"loc":["query","token"],"msg":"Field required"}]}) — vérifié surapi.torbox.app. L'en-tête est donc envoyé en plus du paramètre, jamais à sa place. - Mesures compensatoires appliquées : la requête vers l'API Torbox est strictement côté serveur (l'URL de requête et le token ne sont jamais journalisés dans les journaux du serveur, seuls le code HTTP et le message d'erreur de l'API le sont), et la redirection HTTP 302 vers le permalien CDN de streaming n'est transmise qu'au client Stremio authentifié configuré avec ce compte.
- L'administrateur peut publier un bloc de contenu sous le titre de la page d'accueil
(
embedHtml) ainsi qu'une iframe et un bouton Discord. Ce contenu étant affiché à tous les visiteurs, il est traité comme une entrée non fiable. lib/sanitize.jsapplique un assainissement par reconstruction : la chaîne fournie n'est jamais réémise telle quelle. Elle est tokenisée, filtrée par une liste blanche close, puis reconstruite — tout texte étant échappé.- Balises autorisées uniquement :
p, div, span, br, hr, strong, b, em, i, u, s, small, ul, ol, li, blockquote, code, pre, h1…h6, figure, figcaption, table, thead, tbody, tr, td, th, a, img. Aucunscript,iframe,object,embed,form,style,link,meta,base,svg. - Attributs autorisés uniquement :
href,title(liens) etsrc,alt,title,width,height,loading(images). Aucunon*,style,class,id,srcset. - Les URL sont validées après décodage des références de caractères et suppression des caractères
ignorés (
javascript:,java	script:, casse mixte, attributs sans guillemets), afin qu'aucun schémajavascript:oudata:ne passe. - Les liens sortants sont forcés en
target="_blank" rel="noopener noreferrer nofollow"et la sortie est équilibrée (aucune balise laissée ouverte).
- Balises autorisées uniquement :
- L'iframe éventuelle est HTTPS uniquement et rendue avec
sandbox="allow-scripts allow-same-origin allow-presentation allow-popups"+referrerpolicy="no-referrer": origine isolée, pas d'accès au DOM parent ni aux cookies. - Le bouton Discord n'accepte que les hôtes
discord.gg/discord.comen HTTPS : le champ ne peut pas être détourné en redirection vers un site tiers. - Le champ est borné (4000 caractères pour le HTML, 500 pour l'iframe, 200 pour Discord) et validé par Zod ; l'assainissement est appliqué à l'enregistrement (API admin) et au rendu (défense en profondeur, y compris pour une valeur écrite par un autre chemin).
- Le chargement d'une configuration (
POST /api/user/login, UUID + mot de passe) délivre un jeton de session de 32 octets (randomBytes), conservé côté serveur dans uneMapavec expiration glissante de 1 heure et purge périodique. Il évite de ressaisir le mot de passe à chaque enregistrement depuis/configure. - Portée volontairement limitée : le jeton n'autorise jamais la modification des clés API
(
apiKey,torboxApiKey,prowlarrKey,tmdbKey) ni du mot de passe. Dès qu'un de ces champs change réellement (une valeur vide signifiant « conserver l'existant »), le mot de passe est exigé et la requête est refusée en 401 avec un message explicite. - Le jeton est lié à un UUID (un jeton valide pour un compte ne fonctionne pas pour un autre), transmis via un en-tête dédié (jamais en URL, donc absent des journaux et des référents), et révoqué immédiatement lors d'un changement de mot de passe.
- Côté client, il est conservé dans
sessionStorage(effacé à la fermeture de l'onglet, jamais partagé entre onglets) : en cas de vol via XSS, l'attaquant ne peut modifier que des réglages non sensibles, puisque les clés API restent protégées par le mot de passe.
- La validation d'une clé (
checkAllDebridKey) appelle/v4/usersans le paramètreagent. En effet, AllDebrid interprèteagentcomme une demande de création automatique d'une clé API dédiée dans le compte (cf. Aide AllDebrid — gérer vos clés API), ce qui dupliquait une clé « cinécloud » à chaque vérification. - La clé utilisateur n'est transmise qu'en en-tête
Authorization: Bearer: aucune clé n'est jamais générée ni modifiée côté compte, et aucun secret ne transite en URL.
Si vous découvrez une faille de sécurité dans CinéCloud FR, merci de ne pas ouvrir d'issue publique sur GitHub.
- Utilisez les Advisories de Sécurité GitHub pour soumettre un rapport privé et confidentiel.
- Décrivez avec précision la vulnérabilité observée :
- Type de vulnérabilité (ex: injection, fuite de mémoire, contournement d'authentification)
- Étapes claires de reproduction (PoC / commande curl / script)
- Impact potentiel estimé
- Engagements de l'équipe :
- Accusé de réception sous 48 heures ouvrées.
- Analyse et échange direct pour convenir d'un correctif.
- Publication coordonnée d'un avis de sécurité et d'une nouvelle version corrective sous forme de release taggée.