Actifs, acteurs, frontières de confiance, menaces par composant et risques connus.
/
Statut : implémenté en grande partie, revu le 2026-11-07 (compte rendu, décisions ouvertes et risques restants). Ce document fixe les exigences ; là où il décrit autre chose que le code, le compte rendu fait foi.
Dernière révision : 2026-11-05. Grille de référence : OWASP API Security Top 10 (édition 2023).
ProtoFlix est un serveur multimédia auto-hébergé, installé par un administrateur pour son foyer et ses proches, souvent exposé à Internet derrière un reverse proxy. Le modèle couvre :
le binaire protoflix : le coordinateur (rôle api, seul à ouvrir la base) et les rôles worker et scanner, locaux ou distants (ADR 0020) ;
la base SQLite embarquée protoflix.db (ADR 0019) ;
l'API interne coordinateur ↔ workers (/internal/v1, désactivée par défaut) ;
les processus enfants FFmpeg/ffprobe ;
les stockages montés (disques locaux, SMB/NFS montés par l'OS) ;
les fournisseurs de métadonnées externes ;
les clients (web, puis natifs) via l'API REST /api/v1 et les flux SSE.
le module Téléchargements (HTTP, BitTorrent via démon externe) et les bibliothèques privées par compte.
Hors périmètre : la sécurité du système d'exploitation hôte, du reverse proxy lui-même, du NAS et du réseau domestique, au-delà des recommandations de configuration.
Erreurs, curiosité : tenter d'ouvrir une bibliothèque non partagée, contourner une limite d'âge, lire la bibliothèque privée d'un autre compte
Proche sur un appareil partagé
Accès physique à une TV ou un navigateur où plusieurs comptes sont mémorisés
Deviner ou avoir observé un code rapide à 4 chiffres pour ouvrir le compte d'un autre (enfant vers parent, par exemple)
Invité malveillant
Compte valide fourni par l'administrateur
Énumérer les objets d'autres utilisateurs (BOLA), escalader vers admin, épuiser les ressources (transcodages multiples), exfiltrer des fichiers en masse
Attaquant Internet
Aucun compte, accès au port exposé
Credential stuffing, force brute, scan de vulnérabilités, exploitation d'endpoints non authentifiés, SSRF, lecture de chemins arbitraires
Fichier média hostile
Déposé dans une bibliothèque, envoyé dans une bibliothèque privée, ou récupéré par le module Téléchargements
Exploiter une faille de parsing dans FFmpeg/ffprobe, bombe de ressources (résolution, nombre de pistes, durée), référence à des protocoles réseau (playlists, concat)
Fournisseur de métadonnées compromis ou malveillant
Réponses HTTP consommées par le worker
Contenu injecté (XSS stocké via synopsis), images piégées, redirections vers des adresses internes (SSRF), réponses gigantesques
Pair BitTorrent / source de téléchargement malveillante
Réseau BitTorrent, serveur HTTP distant
Fichiers piégés, noms de fichiers malveillants (../), torrents géants, connexions entrantes vers le client BitTorrent, surveillance des pairs (attribution d'une adresse IP à un partage)
Administrateur applicatif curieux
Rôle admin dans l'application
Consulter l'historique ou la bibliothèque privée d'un autre compte : interdit par l'application (contrôle serveur)
Worker distant compromis, ou jeton de worker volé
Accès réseau à l'API interne avec un jeton valide
Réclamer des jobs pour en lire les payloads, renvoyer des résultats falsifiés (catalogue empoisonné), saturer la file, lire les sources que le worker voit
Administrateur négligent
Accès root à l'hôte, rôle admin
Exposition directe sans TLS, mot de passe faible, TRUSTED_PROXIES trop large, montages en écriture, sauvegardes non testées, exécution en root
flowchart LR
subgraph Internet["Internet / réseau non fiable"]
C[Clients web et natifs]
A[Attaquant]
MP[Fournisseurs de métadonnées]
end
subgraph Edge["Hôte : périmètre exposé"]
RP[Reverse proxy TLS]
end
subgraph Host["Hôte : utilisateur système protoflix"]
API[protoflix rôle api]
WK[protoflix rôles worker / scanner]
FF[Processus FFmpeg / ffprobe<br/>non privilégiés, isolés]
TMP[(Segments temporaires<br/>cache artwork)]
DL[Démon BitTorrent isolé<br/>désactivé par défaut]
Q[(Zone de quarantaine<br/>des téléchargements)]
end
subgraph Data["Données"]
DB[(SQLite protoflix.db<br/>fichier 0600, disque local)]
LIB[(Bibliothèques partagées<br/>montées en lecture seule)]
PRIV[(Bibliothèques privées<br/>par compte, quota)]
end
C -- HTTPS --> RP
A -- HTTPS --> RP
RP -- HTTP local + X-Forwarded-* --> API
API --> DB
RW[Worker distant<br/>autre machine, optionnel] -- "API interne TLS + jeton de worker" --> API
RW -- chemins validés, correspondance source_id --> LIB
API -- chemins validés --> LIB
WK -- chemins validés --> LIB
WK -- arguments typés --> FF
API -- arguments typés --> FF
FF -- lecture seule --> LIB
FF -- écriture --> TMP
API -- lecture --> TMP
WK -- HTTPS sortant filtré --> MP
WK -- HTTP sortant filtré --> SRC[Sources de téléchargement]
DL -- pairs BitTorrent --> PEERS[Pairs BitTorrent]
DL --> Q
WK -- import vérifié --> PRIV
WK -- import vérifié --> LIB
API -- envoi de fichiers --> PRIV
Frontières à défendre explicitement :
Internet → reverse proxy → API : toute requête est non fiable ; les en-têtes X-Forwarded-* ne sont acceptés que depuis PROTOFLIX_TRUSTED_PROXIES.
API → stockage : un identifiant opaque est résolu côté serveur en chemin ; aucun chemin fourni par un client n'est jamais ouvert.
Orchestrateur Go → FFmpeg : FFmpeg traite des données hostiles ; il reçoit des arguments construits depuis un plan typé, jamais une chaîne shell.
Worker → fournisseurs externes : les réponses sont des données non fiables ; les destinations sortantes sont contrôlées (SSRF). Fait pour les métadonnées (lot 2e, internal/platform/outbound) :
redirections vérifiées, HTTPS seul, réponses limitées en taille ;
images servies uniquement en JPEG, PNG ou WebP (pas de SVG), avec Content-Security-Policy: default-src 'none'.
Démon BitTorrent → serveur : le démon et ses fichiers sont non fiables ; le serveur ne fait qu'importer depuis la quarantaine après contrôle.
Coordinateur ↔ worker distant (ADR 0020) : le worker n'est jamais une source de vérité. Il n'accède jamais à la base ; il reçoit des payloads autonomes et renvoie des résultats que le coordinateur valide avant de les appliquer.
Compte ↔ compte : les données privées et la bibliothèque privée d'un compte sont inaccessibles aux autres comptes, admin compris.
Contenu stocké → clients : les métadonnées externes ou manuelles sont rendues comme du texte, jamais comme du HTML.
Limitation par compte et par IP, verrouillage progressif, Argon2id, journal d'audit des échecs
S
Code rapide deviné ou observé sur un appareil partagé
Lié au couple (compte, appareil), valable seulement après une connexion par mot de passe sur cet appareil, 5 essais puis mot de passe exigé, désactivable à distance ; jamais suffisant pour une action sensible
S
Rejeu d'une invitation
Code haché, usage unique, expiration, révocable
I
Admin applicatif lisant historique, « Ma liste » ou bibliothèque privée d'un autre compte
Les services de domaine refusent tout acteur autre que le propriétaire, admin compris ; aucun endpoint admin ne renvoie la liste ni la bibliothèque privée. Exception décidée le 2026-11-07 : l'historique des lectures, effaçable par chaque compte (authn-authz.md §7)
T
CSRF sur actions par cookie
Vérification Origin/Sec-Fetch-Site + jeton CSRF synchronisé pour toute méthode non sûre authentifiée par cookie
T
Modification de champs non autorisés (mass assignment)
Schémas d'entrée explicites par opération, champs inconnus rejetés, champs serveur (rôle, propriétaire, état) jamais lus depuis le corps
Requêtes paramétrées générées par sqlc ; requêtes dynamiques avec paramètres liés uniquement ; pas de SQL concaténé ; extensions SQLite non chargeables (load_extension désactivé)
I
Lecture directe du fichier par un autre utilisateur de l'hôte
protoflix.db, -wal et -shm en 0600, répertoire en 0750, propriétaire protoflix ; aucune base exposée sur le réseau, car il n'y a pas de serveur de base
I
Fuite par les sauvegardes
Fichiers de sauvegarde en 0600 ; avertissement : ils contiennent les données privées des comptes, à chiffrer hors de la machine
T / D
Corruption (copie à chaud partielle, base sur montage réseau, disque plein)
Refus de démarrer sur SMB/NFS ; sauvegarde uniquement par VACUUM INTO ou l'API de sauvegarde ; PRAGMA integrity_check après restauration ; écritures refusées proprement sous un seuil d'espace libre
D
Monopolisation du verrou d'écriture (transaction longue, flot d'écritures)
Une seule connexion d'écriture, transactions courtes, écritures regroupées, busy_timeout borné ; métrique protoflix_db_write_wait_seconds
D
Perte de données
Sauvegardes et restauration testées (docs/operations/backup-restore-upgrade.md)
5.5 bis API interne et workers distants (ADR 0020)#
Cat.
Menace
Mesure prévue
S
Vol ou rejeu d'un jeton de worker
Jeton par worker, stocké haché, révocable ; transmis par fichier, jamais en ligne de commande ; TLS obligatoire hors localhost ; mTLS à évaluer en phase 6
I
API interne exposée à Internet
Écoute distincte (PROTOFLIX_INTERNAL_ADDR) désactivée par défaut, jamais publiée par le reverse proxy ; protoflix doctor alerte si elle est joignable depuis l'extérieur
T
Worker compromis renvoyant des résultats falsifiés (analyse, métadonnées, import)
Le coordinateur valide chaque résultat : schéma, bornes, cohérence avec le job réclamé (identifiant, lease valide, worker détenteur). Le résultat est appliqué de façon idempotente, sans jamais écraser une correction manuelle verrouillée ; le worker émetteur est consigné dans l'audit
I
Worker lisant plus que nécessaire
Le worker ne voit jamais la base ni les données de comptes ; les payloads contiennent (source_id, rel_path) et les seules données utiles au job ; la correspondance des sources est limitée à ce que l'administrateur déclare
T / I
Relais de segments de transcodage
Le jeton de lecture est vérifié par le coordinateur ; le worker n'est jamais joint par les clients ; noms de segments validés (même motif que l'API publique) ; réponses bornées en taille et en durée
D
Worker qui réclame des jobs sans les traiter
Leases courts avec heartbeat, plafond de jobs simultanés par worker, révocation du worker par l'administrateur
E
Version d'API interne incompatible
Version annoncée à l'enregistrement, refus explicite en cas d'incompatibilité
Désactivé par défaut ; activé par l'admin, autorisé compte par compte, destinations limitées (bibliothèque partagée avec droit d'écriture explicite, ou bibliothèque privée du compte). Import et analyse automatiques après téléchargement.
Cat.
Menace
Mesure prévue
I / S
SSRF via lien HTTP fourni par un compte (adresses internes, interface du routeur, NAS)
Même client sortant filtré que les fournisseurs : https/http seulement, refus des adresses privées, loopback, link-local et multicast après résolution DNS et à chaque redirection, redirections limitées, aucun identifiant transmis
T / E
Fichier hostile récupéré puis analysé automatiquement
Téléchargement dans une zone de quarantaine hors bibliothèque ; contrôle de taille et de type ; analyse ffprobe sous le même confinement que les autres fichiers (transcoder-hardening.md)
T
Noms de fichiers malveillants dans un torrent (../, chemins absolus, noms réservés)
Noms recalculés par le serveur ; arborescence normalisée et confinée à la quarantaine ; aucun chemin issu du torrent n'est utilisé tel quel
I
Exposition réseau du client BitTorrent : ports entrants, DHT, IP publique de l'hôte visible des pairs
Client BitTorrent dans un démon externe isolé (processus ou conteneur dédié, compte dédié), sans accès aux bibliothèques ni à la base ; port entrant fermé par défaut ; l'interface admin explique que l'IP publique est visible des pairs
R / juridique
Partage sortant (seed) = redistribution dont l'administrateur est légalement responsable
Seed désactivé par défaut (arrêt à la fin du téléchargement) ; activation explicite avec avertissement ; audit de chaque téléchargement (compte, source, destination)
D
Saturation disque ou bande passante
Quotas par compte et global, limite de débit, nombre de téléchargements simultanés borné, refus sous un seuil d'espace libre
E
Import dans une bibliothèque partagée sans droit
Destination désignée par identifiant et vérifiée dans le service (droit d'écriture explicite), jamais par chemin
Responsabilité légale : le produit ne vérifie pas la licéité des contenus téléchargés. L'activation du module indique que l'administrateur en porte la responsabilité, en cohérence avec l'usage personnel non commercial (PRODUCT.md).
Du code venu d'ailleurs tourne sur le serveur, sur décision de l'administrateur. Ce qui le borne :
Menace
Parade
Ce qui reste
Dépôt volé
Il ne détient que la clé d'index : il peut retenir des mises à jour, pas faire exécuter autre chose
Il peut les retenir tant qu'il sert des index signés, sans que les serveurs le voient : aucune date ne borne la racine
Clé de publication volée
Révocation par la racine, définitive, qui arrête aussi ce qui est installé
Tant qu'un serveur n'a pas relancé de recherche, il ne le sait pas
Clé racine volée
Hors ligne, jamais sur un serveur
Une nouvelle version de ProtoFlix est nécessaire
Retour vers une version vulnérable
Numéros de séquence, d'index et de racine jamais redescendus, y compris après une purge
Supprimer DATA_DIR/modules/.store les efface : il faut déjà les droits du compte
Module non officiel qui se fait passer pour un autre
Identifiant en x., mention permanente, source figée à l'installation
Le nom affiché reste libre
Archive piégée (lien, chemin sortant, bombe)
Dépaquetage par os.Root, types et tailles bornés, modes jamais repris de l'archive
—
Module hostile en fonctionnement
Landlock, seccomp, courtier (ADR 0024)
Sans Landlock, accès complet aux fichiers du compte, dit à l'administrateur avant l'installation
Données d'un module hostile lors d'une copie
Rien n'est suivi, l'illisible est laissé de côté, la taille est vérifiée avant la copie
Son disque n'est pas encore borné, et des données gonflées refusent ses mises à jour : il reste à l'effacer
Module client qui désigne un fichier du serveur
Il ne donne que des noms, ouverts par le serveur sous os.Root dans le dossier réglé par l'administrateur ; liens refusés
—
Module qui vise le réseau local
Avec network_configured, seules les adresses saisies par l'administrateur, sans valeur par défaut : même protocole, même machine, même port, et aucune redirection suivie
—
Sous-titres hostiles ou piégés
Texte UTF-8 seulement, avec des minutages, sans caractère de contrôle, 2 Mio au plus, rangé hors des bibliothèques sous un nom choisi par le serveur
Leur contenu est montré tel quel au lecteur, qui l'affiche comme du texte
Nouveau module qui hérite d'un ancien
Données et réglages effacés à la première installation, secrets compris
—
5.8 Bibliothèque privée et envoi de fichiers (ADR 0018)#
Cat.
Menace
Mesure prévue
I
Fuite entre comptes (lecture, recherche, miniatures, « Reprendre », événements SSE, tableau des lectures admin)
Bibliothèque = Library avec propriétaire ; filtre propriétaire dans le SQL de toutes les requêtes ; artwork et miniatures servis sous contrôle d'accès ; tests « deux comptes » et « admin » systématiques
T / I
Traversée de chemin à l'envoi (../, noms absolus, liens)
Nom de fichier client conservé comme métadonnée seulement ; stockage sous un nom généré par le serveur dans la racine privée du compte
T / E
Fichier hostile envoyé
Type déterminé par analyse du contenu, pas par l'extension ; analyse ffprobe confinée ; images ré-encodées pour les miniatures ; Content-Type fixé par le serveur, X-Content-Type-Options: nosniff, Content-Disposition adapté
D
Dépassement de quota, envoi géant ou sans fin
Taille annoncée obligatoire, quota réservé avant l'envoi et vérifié pendant, envoi reprenable par morceaux bornés, envois incomplets purgés après TTL
I
Admin applicatif consultant le contenu
Interdit côté serveur ; l'admin voit seulement l'occupation du quota (authn-authz.md §7)
Médias, bibliothèques (dont privées), sessions de lecture, progression, « Ma liste », appareils, téléchargements, jobs : tous adressés par identifiant
Identifiants opaques (non devinables, mais ce n'est pas un contrôle d'accès) ; chaque service de domaine reçoit l'acteur et vérifie l'accès à l'objet ; requêtes SQL filtrées par bibliothèques accessibles
Suite de tests « deux utilisateurs » : chaque endpoint appelé avec l'objet de l'autre doit renvoyer 404/403
Argon2id, sessions opaques hachées par (compte, appareil), code rapide lié à l'appareil et limité à 5 essais, invitations hachées à usage unique, verrouillage progressif, MFA TOTP admin (phase 6)
Tests de force brute automatisés, tests d'expiration et de révocation
API3 — Broken Object Property Level Authorization
Champs admin (chemins, arguments FFmpeg, IP) dans des réponses partagées ; champs serveur modifiables
Tests de contrat : aucun champ path ou diagnostic dans les réponses non admin
API4 — Unrestricted Resource Consumption
Transcodage, scans, recherche, téléchargements, verrou d'écriture de la base
Resource Manager, limites de sessions par utilisateur, plafonds de pagination/corps/durée, rate limits, quotas disque
Tests de charge et de saturation, métriques de rejet
API5 — Broken Function Level Authorization
Endpoints /api/v1/admin/*, gestion des utilisateurs, relance de jobs
Rôle vérifié au routeur et dans le service
Tests négatifs pour chaque route admin avec un compte adult et un compte child ; tests où l'admin tente de lire des données privées
API6 — Unrestricted Access to Sensitive Business Flows
Invitations, réinitialisation de mot de passe, module Téléchargements, envoi vers la bibliothèque privée
Invitations à usage unique et expirantes, émises par admin ; Téléchargements désactivés par défaut et autorisés par compte ; quotas par compte ; limites de débit
Tests sur la réutilisation d'invitations
API7 — Server Side Request Forgery
Fournisseurs de métadonnées, URL d'artwork, liens de téléchargement HTTP fournis par les comptes, futures sources IPTV
Client sortant filtré (voir §5.4 et §5.7) appliqué à toute URL, y compris après redirection
Tests avec URL vers 127.0.0.1, 169.254.169.254, ::1, redirections, DNS rebinding
API8 — Security Misconfiguration
Exposition sans TLS, CORS permissif, en-têtes proxy, mode debug
Écoute 127.0.0.1 par défaut, CORS fermé par défaut, TRUSTED_PROXIES vide par défaut, pprof désactivé en production, configuration validée au démarrage
Test de démarrage avec configurations invalides ; diagnostic de configuration admin
API9 — Improper Inventory Management
Endpoints non documentés, anciennes versions
OpenAPI comme source de vérité, test de contrat qui échoue si une route n'est pas documentée ; politique de dépréciation
Test d'inventaire des routes enregistrées contre la spécification
API10 — Unsafe Consumption of APIs
Réponses des fournisseurs de métadonnées
Validation de schéma, tailles bornées, timeouts, contenu traité comme texte, images ré-encodées
Failles FFmpeg : le parsing de médias hostiles est une surface d'attaque large. Le confinement réduit l'impact mais ne l'annule pas ; la réactivité aux mises à jour FFmpeg est une obligation d'exploitation.
Serveur exposé par l'administrateur sans reverse proxy ni TLS : le produit peut avertir (diagnostic de configuration) mais pas l'empêcher entièrement.
Compromission d'un compte administrateur : accès complet par conception. Atténuation : MFA (phase 6), audit, sessions admin plus courtes.
Jetons de lecture dans les URL : ils peuvent apparaître dans les journaux du reverse proxy ou l'historique. Atténuation : courte durée, liaison à la session, révocation à l'arrêt.
Montages SMB/NFS : les permissions effectives dépendent du système hôte ; ProtoFlix ne peut garantir la lecture seule que si l'administrateur monte en lecture seule.
Code rapide à 4 chiffres : 10 000 combinaisons ; une personne qui connaît ou a observé le code ouvre le compte sur cet appareil. Accepté comme commodité locale, limité à l'appareil et à 5 essais, désactivable à distance.
Accès de l'administrateur système aux données privées : la confidentialité entre comptes est appliquée par l'application ; quiconque contrôle l'hôte, la base, le stockage ou les sauvegardes peut lire ces données (pas de chiffrement de bout en bout).
Téléchargements : le produit ne peut ni vérifier la licéité des contenus ni garantir l'absence de fichiers piégés ; le module reste désactivé par défaut.
Worker distant compromis : la validation des résultats limite l'empoisonnement du catalogue, mais un worker compromis lit les fichiers des sources qu'il voit et le contenu des jobs qu'il réclame. Atténuation : n'enrôler que des machines de confiance, jetons révocables, correspondance des sources minimale.
Accès au fichier de base : toute personne qui peut lire protoflix.db ou ses sauvegardes (root, compte protoflix, copie de sauvegarde) lit toutes les données, y compris les données privées des comptes. La base n'est pas chiffrée au repos en V1.
Pas d'audit externe : aucun test d'intrusion n'a encore été réalisé.
Ce modèle est revu à chaque nouvelle famille d'API, avant l'activation du BitTorrent, à l'arrivée de la musique, de la TV en direct (sources IPTV = nouvelle surface SSRF), et avant chaque version publique (phase 6).
Source : docs/security/threat-model.md, dans le dépôt de ProtoFlix.