Spaces:
Sleeping
SECURITY — Picarones
Picarones est conçu pour être déployé dans trois contextes très différents :
- Poste développeur (Codespaces, laptop) — accès local uniquement, le garde-fou est ouvert pour fluidifier l'itération.
- Serveur d'institution (intranet patrimonial, cluster scientifique) — accès authentifié interne, mais quelques utilisateurs peuvent lancer des benchmarks coûteux ; le serveur doit borner la consommation.
- Espace public (HuggingFace Space, démo en ligne) — n'importe quel visiteur peut atteindre l'API ; les clefs serveur (OpenAI, Anthropic, Mistral, Azure…) ne doivent pas être exposées au DoS-financier.
Ce document décrit les contrôles disponibles depuis le Sprint 24 et la configuration recommandée pour chaque cas.
Variables d'environnement de sécurité
| Variable | Défaut | Effet |
|---|---|---|
PICARONES_PUBLIC_MODE |
non défini | Si 1/true : refuse OCR cloud + LLM mutualisés et active rate limit. |
PICARONES_BROWSE_ROOTS |
(auto) | Liste de chemins (séparateur : Unix / ; Windows) autorisés pour /api/corpus/browse. Surcharge le défaut. |
PICARONES_MAX_UPLOAD_MB |
100 |
Taille max d'une image uploadée. |
PICARONES_MAX_CONCURRENT_JOBS |
2 |
Plafond global de benchmarks simultanés (sémaphore en mémoire). |
PICARONES_RATE_LIMIT_PER_HOUR |
5 (en mode public) |
Jobs max par IP et par heure. 0 = désactivé. |
PICARONES_CSP |
(politique durcie) | Surcharge la Content-Security-Policy envoyée par le middleware. |
PICARONES_CSRF_REQUIRED |
non défini | Si 1/true/yes : active la protection CSRF (double-submit cookie + signature HMAC) sur tout POST/PUT/PATCH/DELETE. Voir § « CSRF — déploiement institutionnel » ci-dessous. |
PICARONES_CSRF_SECRET |
(auto) | Secret HMAC pour signer les tokens CSRF. Si non défini, généré au démarrage avec un warning ; les tokens sont alors invalidés à chaque redémarrage. À définir en production. |
CSRF — déploiement institutionnel (Sprint A4)
L'application embarque un middleware CSRF désactivé par défaut (rétrocompat HuggingFace Space où il n'y a pas de session authentifiée à protéger). Pour un déploiement BnF / Bibliothèque nationale derrière SSO :
export PICARONES_CSRF_REQUIRED=1
export PICARONES_CSRF_SECRET="$(openssl rand -hex 32)" # 64 chars hex
Comment ça marche : pattern « double-submit cookie ». Le serveur
pose un cookie picarones_csrf (httponly=False, samesite=strict) qui
contient un token <nonce>.<HMAC-SHA256(secret, nonce)>. Sur tout
POST/PUT/PATCH/DELETE non exempt, le client doit renvoyer le même
token dans l'en-tête X-CSRF-Token. Le serveur compare en temps
constant et vérifie la signature. Une page tierce ne peut pas lire
le cookie (samesite=strict + JS d'origine différente) ni produire
une signature valide (HMAC), donc ne peut pas forger une requête.
Endpoints exemptés : /health, /api/csrf/token (le endpoint
qui donne le token).
Bootstrap d'un client tiers (curl, scripts CI) :
# 1. Récupérer un token et persister le cookie dans un jar
curl -c cookies.txt http://picarones.example/api/csrf/token | jq -r .token
# 2. Réutiliser le token dans le header
TOKEN=$(jq -r .token < <(curl -sb cookies.txt http://.../api/csrf/token))
curl -b cookies.txt -H "X-CSRF-Token: $TOKEN" -X POST .../api/lang/fr
Frontend : le JS embarqué (web-app.js) wrappe fetch() pour
injecter automatiquement le header sur toute requête mutante
same-origin. Aucun changement requis dans le code applicatif.
Contrôles par contexte
🧑💻 Développement (défaut, PICARONES_PUBLIC_MODE non défini)
picarones serve --port 8000
- Tous les moteurs OCR sont disponibles.
/api/corpus/browsevoitcwd,./uploads/,/workspaces,tempdir.- Pas de rate limit.
- CSP appliquée mais permissive (
unsafe-inlinetoléré tant que les templates web ne sont pas Jinja2 — voir Sprint 25).
🏛 Serveur d'institution
export PICARONES_BROWSE_ROOTS="/srv/corpus:/srv/uploads"
export PICARONES_MAX_CONCURRENT_JOBS=4
export PICARONES_MAX_UPLOAD_MB=500
picarones serve --host 0.0.0.0 --port 8000
À combiner avec une terminaison TLS et une authentification au niveau reverse-proxy (nginx + auth basic, ou Keycloak/SAML). Picarones n'embarque pas son propre système d'authentification — c'est un choix conscient pour ne pas réinventer un sous-système qui sera mieux servi par l'infra existante.
🌐 HuggingFace Space / démo publique
ENV PICARONES_PUBLIC_MODE=1
ENV PICARONES_RATE_LIMIT_PER_HOUR=5
ENV PICARONES_MAX_CONCURRENT_JOBS=2
ENV PICARONES_MAX_UPLOAD_MB=50
# Optionnel : surcharger les browse roots
# ENV PICARONES_BROWSE_ROOTS=/data/corpus
Effets en mode public :
- ❌ Moteurs OCR cloud (
mistral_ocr,google_vision,azure_doc_intel) refusés en403. - ❌ Pipelines OCR+LLM (
openai,anthropic,mistral,ollama) refusés en403. - ❌
/api/corpus/browsese limite à./uploads/. - ⏱
/api/benchmark/startet/api/benchmark/runrate-limités en429. - 🔒
Content-Security-Policy+X-Frame-Options: DENY+X-Content-Type-Options: nosniff+Referrer-Policy: strict-origin-when-cross-originsur toutes les réponses.
Contrôles d'upload
Images
- Validation Pillow systématique :
Image.open(...).verify()dans untry/exceptqui capture lesUnidentifiedImageError,DecompressionBombError, et l'exception générique. - Limite de taille par fichier (
PICARONES_MAX_UPLOAD_MB). - Basename forcé : un nom de fichier multipart contenant
..ou/est tronqué à son nom de base avant écriture.
Archives ZIP
- Bombe ZIP : taille décompressée bornée à 500 Mo, nombre de fichiers borné à 2000.
- Path traversal : seuls les noms de base sont conservés (les répertoires internes du ZIP sont aplatis).
- Filtres macOS : les fichiers
._*(AppleDouble) sont ignorés. - Symlinks : Python's
zipfilen'extrait pas les symlinks par défaut ; un check explicite (ZipInfo.external_attr & 0xA000) est sur la roadmap comme défense en profondeur.
Modèle de menace
| Menace | Mitigation |
|---|---|
| Visiteur consomme la clef API mainteneur | PICARONES_PUBLIC_MODE=1 → 403 sur LLM/OCR cloud. |
| DoS via 50 benchmarks concurrents | PICARONES_MAX_CONCURRENT_JOBS (sémaphore) + rate limit par IP. |
Bombe Pillow (CVE-2023-50447 & cie) |
Image.verify() levant DecompressionBombError. |
| Path traversal sur browse / image / delete | Validation explicite + résolution + check is_relative_to. |
Exfiltration via browse /etc ou /root |
PICARONES_BROWSE_ROOTS restreint, défaut public limité à uploads. |
| XSS via paramètres URL | CSP default-src 'self', frame-ancestors 'none'. |
| Clickjacking | X-Frame-Options: DENY. |
Reporting de vulnérabilités
Les vulnérabilités potentielles peuvent être ouvertes via une Security Advisory GitHub (privée par défaut) sur github.com/maribakulj/Picarones.
Merci de ne pas divulguer publiquement avant qu'un correctif ne soit disponible. Les contributeurs prendront en charge la triage en moins de 14 jours.