Explorer
KNOW-PAT-057

Architecture de débridage torrent - Magnet vers téléchargement direct

Domaine
cybersecu
Type
pattern
Priorité
P2

Parent : [[INDEX-CYBERSECU]]

Architecture de débridage torrent - Magnet vers téléchargement direct

Problème

Comment convertir un lien magnet en téléchargement direct HTTP ? L'archive Ygg contient un système complet de débridage torrent avec architecture distribuée, workers, caching, et seeding automatisé.

Contexte archive Ygg

  • Projet : 01_SOURCE_CODE/02g_debrideur/
  • Stack : Node.js + Express + MariaDB + Redis + BullMQ + qBittorrent-nox
  • Fichiers : 20+ fichiers source (app.js, torrentWorker.js, qbittorrentService.js, etc.)
  • Architecture : Load balancer (Nginx) → Web servers (Express) → Redis (queue/cache) → Workers → qBittorrent → Storage (NFS/S3)

Architecture complète

┌─────────────────┐
│   CLIENTS       │  (Navigateurs Web)
└────────┬────────┘
         │ HTTP/SSE
         ▼
┌─────────────────┐
│  LOAD BALANCER  │  (Nginx)
└────────┬────────┘
         │
    ┌────┴────┐
    ▼         ▼
┌────────┐ ┌────────┐
│ Express│ │ Express│  (Web Servers)
└───┬────┘ └───┬────┘
    │          │
    └────┬─────┘
         │
    ┌────┴────┬────────┐
    ▼         ▼        ▼
┌────────┐ ┌────────┐ ┌────────┐
│  Redis │ │ MariaDB│ │qBittor.│
│ Queue  │ │  (DB)  │ │ (API)  │
└───┬────┘ └────────┘ └────────┘
    │ BullMQ Jobs
    ▼
┌─────────────────┐
│    WORKERS      │  (Torrent Download Workers)
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  STORAGE (NFS)  │  /storage/downloads
└─────────────────┘

Flux de téléchargement

  1. Soumission : L'utilisateur soumet un lien magnet via le formulaire web
  2. Parsing : Extraction de l'infoHash (hex 40 caractères ou base32 32 caractères)
  3. Vérification cache : Le système vérifie si le torrent existe déjà en base
  4. Queue : Si nouveau, le job est ajouté à la queue BullMQ Redis
  5. Téléchargement : Le worker ajoute le torrent à qBittorrent via API
  6. Progression : Les mises à jour sont publiées via Redis Pub/Sub → SSE → client
  7. Complétion : Les fichiers sont disponibles au téléchargement direct
  8. Seeding : Le torrent continue à seeder selon les limites configurées (ratio/temps)
  9. Cleanup : Les torrents seedés sont supprimés automatiquement

Pattern protectif — Détection de systèmes de débridage

1. Signatures d'un service de débridage

# detect_debrid_service.py — Détecter les signaux d'un service de débridage
import re

class DebridDetector:
    SIGNALS = {
        # URLs de téléchargement direct avec tokens courts
        'direct_download_tokens': re.compile(r'/dl/[a-zA-Z0-9_-]{10,32}'),
        
        # Endpoints de soumission magnet
        'magnet_submission': re.compile(r'/(add|submit|download)\?magnet='),
        
        # APIs torrent
        'torrent_api': re.compile(r'/api/v\d+/(torrent|magnet|debrid)'),
        
        # Headers de réponse suspects
        'debrid_headers': ['X-Debrid-Cache', 'X-Torrent-Hash', 'X-Seed-Time'],
        
        # Ports qBittorrent exposés
        'qbittorrent_port': 8080,
        
        # URLs de tracking de progression SSE
        'sse_progress': re.compile(r'/sse/(torrent|progress|download)/'),
    }
    
    def analyze_response(self, response):
        signals = []
        
        # Vérifier les headers
        for header in self.SIGNALS['debrid_headers']:
            if header in response.headers:
                signals.append(f"Header debrid: {header}")
        
        # Vérifier le contenu
        body = response.text
        if 'magnet:?' in body and 'download' in body:
            signals.append("Formulaire magnet détecté")
        
        if 'sse' in body and 'torrent' in body:
            signals.append("Streaming progression torrent")
        
        return signals

2. Protection contre l'abus d'un service de débridage

# anti_debrid_abuse.py — Rate limiting et validation
from flask import Flask, request, abort
import redis
import hashlib

app = Flask(__name__)
redis_client = redis.Redis()

# Rate limiting par IP
DOWNLOAD_RATE_LIMIT = 10  # 10 téléchargements / heure
MAGNET_RATE_LIMIT = 5     # 5 magnets / heure

@app.route('/download/<token>')
def download(token):
    ip = request.remote_addr
    
    # Vérifier le token
    if not validate_token(token):
        abort(404)
    
    # Rate limit
    key = f"dl:{ip}:{token}"
    count = redis_client.incr(key)
    redis_client.expire(key, 3600)
    
    if count > DOWNLOAD_RATE_LIMIT:
        abort(429, 'Too many downloads')
    
    return send_file(token)

@app.route('/add', methods=['POST'])
def add_magnet():
    ip = request.remote_addr
    magnet = request.form.get('magnet')
    
    # Validation magnet
    if not magnet.startswith('magnet:?'):
        abort(400)
    
    info_hash = extract_info_hash(magnet)
    if not info_hash or len(info_hash) != 40:
        abort(400)
    
    # Rate limit par infoHash
    hash_key = f"magnet:{ip}:{info_hash}"
    hash_count = redis_client.incr(hash_key)
    redis_client.expire(hash_key, 3600)
    
    if hash_count > MAGNET_RATE_LIMIT:
        abort(429, 'Too many magnets')
    
    # Blacklist de torrents connus (DMCA, malware)
    if info_hash in BLACKLIST:
        abort(403, 'Torrent blacklisted')
    
    return process_magnet(magnet)

3. Parsing sécurisé de liens magnet

// magnetParser.js — Parsing et validation sécurisée
function parseMagnet(magnetUri) {
  if (!magnetUri || typeof magnetUri !== 'string') {
    return null;
  }
  
  // Vérifier le schéma
  if (!magnetUri.startsWith('magnet:?')) {
    return null;
  }
  
  // Vérifier la longueur max (prévention DoS)
  if (magnetUri.length > 2048) {
    return null;
  }
  
  const result = {
    infoHash: null,
    name: null,
    announce: [],
    size: null
  };
  
  try {
    const params = magnetUri.slice(8).split('&');
    
    for (const param of params) {
      const [key, value] = param.split('=');
      const decodedValue = decodeURIComponent(value || '');
      
      if (key === 'xt' && decodedValue.startsWith('urn:btih:')) {
        let hash = decodedValue.slice(9);
        
        // Validation hex (40 caractères)
        if (hash.length === 40 && /^[a-fA-F0-9]+$/.test(hash)) {
          result.infoHash = hash.toLowerCase();
        }
        // Validation base32 (32 caractères)
        else if (hash.length === 32 && /^[A-Z2-7]+$/.test(hash)) {
          result.infoHash = base32ToHex(hash);
        }
      } else if (key === 'dn') {
        // Validation nom (pas de path traversal)
        if (!decodedValue.includes('..') && !decodedValue.includes('/')) {
          result.name = decodedValue;
        }
      } else if (key === 'tr') {
        // Validation URL tracker
        if (isValidTrackerUrl(decodedValue)) {
          result.announce.push(decodedValue);
        }
      }
    }
  } catch (e) {
    return null;
  }
  
  return result.infoHash ? result : null;
}

Stack technique identifiée

Composant Technologie Rôle
Backend Node.js + Express Serveur web et API
Frontend EJS + Tailwind CSS + Alpine.js Interface utilisateur
Base de données MariaDB Stockage persistant
Cache/Queue Redis + BullMQ File d'attente et cache
Client Torrent qBittorrent-nox Téléchargement et seeding
Temps réel SSE + Redis Pub/Sub Mises à jour en direct
Auth bcryptjs + express-session Authentification utilisateurs

Checklist de validation

  • Parsing magnet avec validation stricte (hex/base32)
  • Rate limiting par IP + infoHash
  • Blacklist de torrents DMCA/malware
  • Tokens de téléchargement temporaires (24h max)
  • Nettoyage automatique des fichiers après expiration
  • Seeding limité (ratio + temps max)
  • Isolation des workers (pas d'accès direct au storage)
  • Validation des chemins de fichiers (pas de path traversal)
  • Headers de sécurité (CSP, HSTS, X-Frame-Options)
  • Audit des téléchargements (qui, quoi, quand)

Anti-patterns associés

  • KNOW-ANT-007 — Hardcoded secrets in .env files

Comment se prot[ée]ger

  • Validation stricte des URLs soumises (sch[ée]ma, domaine, pas d'internal)
  • Rate limiting par utilisateur + par IP
  • Isolation r[ée]seau du worker debrid (pas d'acc[è]s interne)
  • Logging et audit des t[ée]l[ée]chargements
  • Scan antivirus des fichiers t[ée]l[ée]charg[ée]s

Références