État détaillé de la sécurité technique et juridique de la suite RECOV / RECOVMAX. Document destiné aux DPO, responsables sécurité, dirigeants de cabinets qui veulent comprendre ce qui est en place avant d'utiliser le service. Ce document est public et imprimable en PDF (Ctrl + P) pour archivage.
verify_audit_chain()| Contrôle | Statut | Détail |
|---|---|---|
| RLS PostgreSQL multi-tenant | ✓ En place | Toutes les tables métier (creances, clients, debiteurs, dossier_events, promesses_paiement, echeanciers, templates) appliquent auth.uid() = user_id. Un cabinet ne peut techniquement pas voir les données d'un autre — la base elle-même refuse. |
| RLS read-only enforcement | ✓ En place | Migration 031 (mai 2026) : helper is_user_writable() bloque INSERT/UPDATE/DELETE quand subscription_status ∉ (active, grace). Les comptes archived/purged ne peuvent que lire. |
| RLS plan enforcement | ✓ En place | Migration 032 : quotas (50 clients, 50 créances, 1 000 relances) appliqués au niveau base, pas seulement applicatif. |
| Service-role isolé serverless | ✓ En place | La clé SUPABASE_SERVICE_ROLE_KEY n'apparaît jamais côté client. Uniquement utilisée dans les fonctions Vercel serverless (admin.js, webhook.js, generate.js). |
| Migrations versionnées | ✓ En place | 43 migrations SQL versionnées dans Git, idempotentes (DROP IF EXISTS, CREATE OR REPLACE, ALTER...IF NOT EXISTS). |
| Backups automatisés & procédure restore | ✓ | Cron quotidien /api/backup?action=cron_weekly (Vercel 3h UTC) — 17 tables critiques exportées en JSON, envoyé par email TLS à l'opérateur. Procédure de restauration documentée v1.0 (13/05/2026) avec 4 scénarios (1 ligne · 1 table · base complète · compromission), vérifications d'intégrité post-restore (verify_audit_chain(), FK, comptes), calendrier trimestriel. Premier test checklist validé le 13/05/2026 (backup généré + reçu + hash chain validée). Engagements : RPO 24h · RTO 48h (formalisés dans DPA prochaine révision). |
| Audit append-only avec chaînage cryptographique | ✓ | Migration 052 (13/05/2026) — colonnes prev_hash + event_hash SHA-256 sur app_audit_log calculées via trigger BEFORE INSERT. Toute altération a posteriori d'une ligne casse la chaîne et est détectable via RPC verify_audit_chain() qui retourne {valid, broken_at_id, reason}. Genesis hash GENESIS pour la première ligne. Extension pgcrypto Supabase. Service_role uniquement pour la RPC de vérification. |
| Header | Statut | Valeur appliquée |
|---|---|---|
| Strict-Transport-Security (HSTS) | ✓ | max-age=63072000; includeSubDomains; preload (2 ans, sous-domaines, preload list) |
| X-Frame-Options | ✓ | DENY — pas d'embed possible (anti-clickjacking) |
| X-Content-Type-Options | ✓ | nosniff — anti-MIME sniffing |
| Content-Security-Policy | ✓ | Whitelist stricte : self + cdn.jsdelivr.net + cdnjs + unpkg + js.stripe + supabase + plausible. frame-src https://js.stripe.com uniquement. connect-src limité aux domaines explicites. |
| Referrer-Policy | ✓ | strict-origin-when-cross-origin — pas de leak de chemins lors de navigation externe |
| Permissions-Policy | ✓ | camera=(), geolocation=(), interest-cohort=() — APIs sensibles désactivées + opt-out FLoC |
| X-Robots-Tag (pages sensibles) | ✓ | noindex, nofollow, noarchive, nosnippet sur /admin, /recovmax-dashboard, /partner-report |
| Cache-Control (pages auth) | ✓ | private, no-store, no-cache, must-revalidate, max-age=0 — pas de cache sur les pages connectées |
| Cross-Origin-Opener-Policy | ⚠ Non configuré | À ajouter en P1 pour isolation totale du contexte de navigation (SAB / process isolation) |
CSP 'unsafe-inline' |
⚠ Présent | Nécessaire actuellement pour les inline-styles HTML. Mitigation : tous les innerHTML avec données user passent par esc() qui échappe & < > ". Refactor vers nonces possible mais lourd (5+ jours). |
| Contrôle | Statut | Détail |
|---|---|---|
| Supabase Auth (PostgreSQL backed) | ✓ En place | Email + mot de passe, magic links, OAuth Google. JWT signés HS256. Tokens d'accès en session storage, refresh tokens en cookie httpOnly côté Supabase. |
| Hash des mots de passe | ✓ En place | bcrypt côté Supabase (workfactor 10). Aucun mot de passe en clair côté DEZVOLTA. |
| Rate limiting login | ⚠ Backend Supabase uniquement | Pas de rate limit applicatif persistant côté Vercel (cold start reset toutes les 5 min). Supabase a son propre rate limit backend (5 tentatives / 5 min / IP). À renforcer en P1 avec Upstash Redis (gratuit jusqu'à 10k requêtes/jour). |
| Honeypot anti-bot signup | ✓ En place | Champ caché gateWebsite dans le formulaire d'inscription, rempli par les bots, vérifié côté serveur. |
| 2FA (TOTP) | À faire P1 | Supabase Auth supporte TOTP nativement. Sprint estimé : 2-3 jours. Recommandé avant 100 utilisateurs payants. |
| CAPTCHA login | À faire P1 | Cloudflare Turnstile ou hCaptcha. Sprint estimé : 1 jour. Mitigation actuelle : rate limit Supabase backend. |
| Tokens API admin | ✓ En place | ADMIN_TOKEN (HMAC bearer) pour les endpoints sensibles. Secret stocké en env Vercel, jamais en code. |
| Vecteur | Statut | Mitigation |
|---|---|---|
| XSS — escape HTML user-controlled | ✓ En place | Fonction esc() utilisée 129× dans le dashboard pour échapper & < > " sur toute donnée provenant de la base. Pattern systématique : ${esc(variable)}. |
| SQL injection | ✓ Couvert | Requêtes via Supabase JS SDK (paramétrées) ou via supabaseQuery() côté serverless. Aucune concaténation SQL côté code applicatif. RPCs côté Postgres en SECURITY DEFINER avec params typés. |
| CSRF dashboard | ✓ Couvert | Authentification par JWT en header Authorization: Bearer (pas de cookie session) → CSRF par formulaire impossible. SameSite cookies sur Supabase Auth. |
| CSRF webhooks Stripe | ✓ Couvert | Signature HMAC SHA-256 vérifiée sur chaque event via STRIPE_WEBHOOK_SECRET. Rejet immédiat si signature invalide. |
| CSRF webhooks partenaires | ✓ Couvert | HMAC mutuelle via partner.webhook_secret. Header X-Recov-Signature sur les réponses sortantes. |
| Open redirect | ✓ Couvert | Tous les redirects (login, magic link, Stripe checkout) utilisent des URLs hardcodées vers le domaine recov.pro. Aucun paramètre user-controlled dans les redirects. |
| Exigence RGPD | Statut | Détail |
|---|---|---|
| Hébergement Union européenne | ✓ Conforme | Supabase région eu-central-1 (Frankfurt). Vercel UE pour les fonctions sensibles. Cloudflare CDN POP UE. |
| Transferts hors UE encadrés | ✓ Conforme | Anthropic (USA, API Claude) : Clauses Contractuelles Types Commission UE 2021/914 + Zero Data Retention. Stripe Irlande (UE). Cloudflare : POPs UE forcés. |
| Liste publique des sous-traitants (art. 28-2) | ✓ Conforme | Page recov.pro/sous-traitants mise à jour à chaque ajout/retrait. Préavis 15 jours pour droit d'opposition. |
| DPA spécifique RECOVMAX | ✓ Conforme | 15 articles couvrant la sous-traitance à 3 niveaux (RECOV → Cabinet → Client TPE). Voir DPA RECOVMAX. |
| Mandat de gestion fourni | ✓ Conforme | Template PDF généré par RECOVMAX (7 articles, références L441-10 + DGCCRF). Le cabinet doit le signer avec chacun de ses clients TPE avant traitement. |
| Droits des personnes (art. 15-22) | ✓ Conforme | Accès, rectification, effacement, portabilité, limitation, opposition. Procédure : pedro.berbel@dezvolta.org. Délai 30 jours. |
| Pseudonymisation analytics | ✓ Conforme | PostHog : userId hashé, IP anonymisée, blacklist explicite (email, nom, SIRET). Aucune PII transmise. |
| Retention policy automatisée | À faire P2 | Pas de purge auto après inactivité. Recommandé : 3 ans après dernière connexion. Sprint estimé : 1 jour (cron). |
| DPO désigné | Non requis | Pas obligatoire en l'état (volume < 250 users, pas de données sensibles RGPD strict art. 9). Recommandé au-delà de 250 utilisateurs ou en cas de saisine CNIL. Budget : 50-150 €/mois externalisé. |
| Procédure incident (notification 72h) | À faire P2 | Pas de runbook formel pour notification CNIL en cas de violation. À documenter (modèle CNIL fourni). |
| PCI DSS | ✓ N/A direct | Aucune donnée carte ne transite chez DEZVOLTA. Stripe Payments Europe Ltd (Irlande, PCI DSS Level 1) gère intégralement la collecte et le stockage des moyens de paiement. |
| Webhook Stripe — signature | ✓ | HMAC SHA-256 vérifié via STRIPE_WEBHOOK_SECRET (env Vercel). Rejet immédiat si invalide. Idempotence sur stripe_payment_id. |
| Customer Portal (annulation, MAJ CB) | ✓ | Géré 100% par Stripe (UI hosted). DEZVOLTA n'a jamais accès aux données carte. |
| Mode TEST séparé du LIVE | ✓ | Environnements isolés. Bascule LIVE = changement de variables d'env Vercel uniquement, pas de code. |
| BYO Stripe Payment Link — anti-phishing | ✓ | Migration 049 (11/05/2026) : contrainte CHECK whitelist ^https://buy.stripe.com/[A-Za-z0-9_/-]+$ sur les liens fournis par les utilisateurs cabinet. Impossible d'injecter une URL de phishing dans les emails de relance envoyés à leurs débiteurs. |
| Transparence débiteur | ✓ | Le débiteur paye sur buy.stripe.com avec le branding du créancier final. Zéro mention RECOVMAX ni du cabinet dans le parcours de paiement. L'argent va directement au compte Stripe du créancier — DEZVOLTA n'est pas intermédiaire de paiement (pas d'agrément PSP/ACPR requis). |
| Hard cap fondateurs — race-safe | ✓ | Migration 050 (11/05/2026) : RPC claim_founder_seat() avec lock SELECT ... FOR UPDATE sur app_config. Deux paiements concurrents au cap-1 ne peuvent pas dépasser la limite. Fallback Standard + audit warning en cas de cap déjà atteint. |
| Réconciliation Stripe native | ✓ | client_reference_id Stripe injecté automatiquement dans les Payment Links et QR codes (MED + LRAR PDF). Permet à terme une réconciliation automatique sans intermédiaire technique. |
Recovo est l'assistant IA intégré au dashboard RECOVMAX pour la rédaction de relances et la synthèse de dossiers, propulsé par Anthropic Claude (sous-traitant documenté dans la liste publique sous-traitants).
| Anthropic ZDR (Zero Data Retention) | ✓ | Aucun prompt utilisé pour entraîner les modèles. Conservation côté Anthropic = 0 jour. Accord ZDR signé entre DEZVOLTA et Anthropic. |
| Filtre PII avant appel IA | ✓ | Aucun email débiteur ni nom de client final n'est transmis dans les prompts. Filtre serveur côté API avant tout appel. |
| Disclaimer juridique systématique | ✓ | Chaque réponse Recovo inclut explicitement : « RECOVO oriente la rédaction. Pour conseil juridique, voir avocat ou commissaire de justice. » |
| Sources internes figées | ✓ | Cadre français : L.441-10, loi 2026-307, Factur-X. Pas d'hallucination juridique sur réglementations étrangères. |
| Positionnement clair | ✓ | Recovo est un assistant d'orientation et de synthèse, pas un conseiller juridique automatisé. Le professionnel garde la responsabilité de chaque envoi. |
| Redaction des logs | ✓ | Stricte côté serveur : pas d'email débiteur, nom client, SIRET dans les traces de monitoring ou logs Vercel. |
| Anti prompt injection | ⚠ En cours P1.3 | Filtre input utilisateur (rejet des patterns "ignore previous instructions", "system:", etc.) en livraison sprint sécurité semaine 12 mai 2026. |
Au-delà des contrôles statiques (architecture, headers), un middleware Vercel Edge tourne en permanence devant chaque requête. Il bloque les scrapers, AI bots non autorisés et outils offensifs avant même qu'ils n'atteignent l'application.
| Catégorie bloquée | Exemples | Justification |
|---|---|---|
| Scrapers Python génériques | scrapy, python-httpx, python-requests |
Aucun usage légitime sur un SaaS — uniquement des scripts d'extraction massive de contenu. |
| AI scrapers non autorisés | gptbot, ccbot, perplexitybot, cohere-ai |
Ces bots utilisent le contenu pour entraîner des modèles concurrents sans contrepartie. Anthropic (notre sous-traitant IA) est exempté car couvert par DPA. |
| SEO crawlers agressifs | semrushbot, ahrefsbot, mj12bot, bytespider |
Crawl à haute fréquence sans valeur ajoutée pour les utilisateurs. Ralentissent l'application. |
| Outils offensifs | nikto, nmap, nuclei, zgrab, masscan |
Outils de pentest publics — leur présence indique une tentative active de cartographie ou exploitation. |
| Crawlers SEO secondaires | dotbot, petalbot, blexbot, seokicks |
Sources de bruit sans valeur pour les utilisateurs. Bloqués pour préserver les ressources Edge. |
Statistiques sur 7 jours — accessibles en temps réel dans le dashboard admin interne :
| Métrique | Valeur observée 7j | Lecture |
|---|---|---|
| Requêtes bloquées | ~ 200 | Tentatives de scraping rejetées avant traitement |
| Crawlers légitimes autorisés | ~ 400 | Google, LinkedIn, Apple : indexation normale |
| Top scraper bloqué | gptbot | OpenAI tente régulièrement — toujours rejeté |
| Pays de provenance des scrapers | US, BR, NL, PT, CN, TH, ID, TR, GB, BE | Profil mondial typique des pools de proxies |
| Vecteurs distincts détectés | 8 types | UA pattern, IP pattern, comportement anormal |
| Variables d'environnement Vercel | ✓ | Tous les secrets stockés en env vars Vercel (chiffrement at-rest). Aucun secret en code (vérifié par grep + secrets scanning GitHub Push Protection). |
| Rotation des secrets | ⚠ Manuelle | Pas de rotation automatique. Procédure manuelle documentée. À industrialiser en P2 (rotation tous les 6 mois recommandée). |
| Anon key Supabase publique | ✓ | L'anon key est par design publique. Sécurité reposant intégralement sur les policies RLS. Service-role key strictement serverless. |
| Sprint | Action | Bénéfice |
|---|---|---|
| P1 · Q3 2026 | 2FA TOTP optionnelle | Protège contre les comptes compromis (mot de passe leaké) |
| P1 · Q3 2026 | CAPTCHA login (Turnstile) | Anti brute-force applicatif persistant |
| P1 · Q3 2026 | Rate limit persistant (Upstash Redis) | Remplace le rate limit in-memory Vercel |
| P2 · Q4 2026 | Headers COOP/COEP | Isolation totale du contexte navigation |
| P2 · Q4 2026 | Audit log applicatif admin (table append-only avec chaînage) | Traçabilité juridique des actions admin |
| P2 · Q4 2026 | Test restauration backup | Validation effective de la stratégie DRP |
| P2 · Q4 2026 | Retention auto (purge 3 ans) | Conformité RGPD art. 5-1-e |
| P2 · Q4 2026 | Runbook incident + notification CNIL | Procédure documentée 72h |
| P3 · Q1 2027 | Pentest externe (1 500-3 000 €) | Audit indépendant tiers |
| P3 · Q1 2027 | Audit juridique CGU/DPA externe (800-1 500 €) | Validation par avocat tech-RGPD |
| P3 · Q2 2027 | DPO externalisé si > 250 users | Conformité art. 37 RGPD si volume / sensibilité |
Si vous avez identifié une vulnérabilité dans RECOV ou RECOVMAX, merci de nous écrire à pedro.berbel@dezvolta.org avec l'objet [SECURITY].
Hors périmètre : DoS volumétrique, ingénierie sociale, accès physique, services tiers (Stripe, Supabase, Vercel, Resend, Anthropic, Cloudflare — à signaler directement à leur équipe sécurité respective).
Format standard : ces informations sont également publiées au format RFC 9116 — voir recov.pro/.well-known/security.txt.
DEZVOLTA s'engage à rendre la suite RECOV / RECOVMAX accessible au plus grand nombre. L'audit professionnel accessibilité partielle (audit en cours) / RGAA 4.1 n'a pas encore été réalisé — nous publions ici une auto-déclaration partielle honnête, mise à jour au fil des améliorations.
Date de la déclaration : 11 mai 2026. Méthode : auto-évaluation interne (sans audit externe). Un audit professionnel sera réalisé dans les 12 mois.
| Critère | Niveau | État |
|---|---|---|
Hiérarchie de titres + langue de la page (lang="fr") | WCAG A · 1.3.1 / 3.1.1 | ✓ Conforme |
Labels formulaires associés (label for=) | WCAG A · 3.3.2 | ✓ Conforme |
| Navigation 100 % au clavier (Tab, Enter, Espace, Échap) | WCAG A · 2.1.1 | ✓ Conforme |
| Skip link "Aller au contenu principal" | WCAG A · 2.4.1 | ✓ Conforme |
| Trap focus dans les drawers / modales | WCAG A · 2.4.3 | ✓ Conforme |
| Focus visible (outline lime) | WCAG AA · 2.4.7 | ✓ Conforme |
aria-hidden sur emojis décoratifs | WCAG A · 1.1.1 | ✓ Conforme |
aria-label enrichi sur composants drag | WCAG A · 4.1.2 | ✓ Conforme |
Annonces lecteurs d'écran (aria-live) | WCAG AA · 4.1.3 | ✓ Conforme |
Identification erreurs forms (aria-invalid, role="alert") | WCAG A · 3.3.1 | ✓ Conforme (login) |
| Alternative clavier au glisser-déposer Kanban (touche Espace) | WCAG AA · 2.5.7 | ✓ Conforme |
prefers-reduced-motion respecté | WCAG AAA · 2.3.3 | ✓ Conforme |
| Tooltips dismissable par Échap | WCAG AA · 1.4.13 | ✓ Conforme (partiel) |
| Mode confort de lecture (zoom utilisateur, contraste renforcé, animations off) | Bonus utilisateur | ✓ Disponible |
| Compatible NVDA, JAWS, VoiceOver, TalkBack | WCAG A · 4.1.2 | ⚠️ Théoriquement OK · tests utilisateurs réels à faire |
| Critère | Niveau | État |
|---|---|---|
| Audit professionnel externe (cabinet RGAA) | — | ❌ Non réalisé · prévu dans les 12 mois |
| Contraste texte (4,5:1) audité partout | WCAG AA · 1.4.3 | ⚠️ Audit partiel · variable --lime-text introduite mais non auditée systématiquement |
| Contraste éléments non-texte (3:1) | WCAG AA · 1.4.11 | ⚠️ Non audité |
| Resize text 200 % sans perte de fonction | WCAG AA · 1.4.4 | ⚠️ Non testé sur l'ensemble des écrans |
| Reflow 320 px CSS sans scroll horizontal | WCAG AA · 1.4.10 | ⚠️ Partiellement testé · ajustements en cours |
| Suggestions structurées sur erreurs (au-delà du message texte) | WCAG AA · 3.3.3 | ❌ Non implémenté |
| Tests réels utilisateur avec lecteurs d'écran | — | ❌ Non réalisés (programmés post-30 fondateurs) |
| Sous-titres / transcripts pour contenu vidéo | WCAG A · 1.2.x | ⚠️ Pas de vidéo embarquée actuellement · sera fait si vidéo ajoutée |
Si vous rencontrez un défaut d'accessibilité (élément non navigable au clavier, contraste insuffisant, contenu mal lu par un lecteur d'écran, etc.), écrivez-nous à pedro.berbel@dezvolta.org avec :
Engagement de réponse : sous 5 jours ouvrés. Correction : selon la gravité, entre 1 jour (bloquant) et 30 jours (amélioration).
Cette procédure est à utiliser dans le cas suivant : vous avez signalé au responsable du site un défaut d'accessibilité qui vous empêche d'accéder à un contenu ou à un service, et vous n'avez pas obtenu de réponse satisfaisante. Vous pouvez :
Ce document décrit l'état de la sécurité technique et juridique au 11 mai 2026. Il est mis à jour à chaque évolution structurelle. Il n'engage pas DEZVOLTA sur des résultats ni sur l'absence de failles non identifiées. Pour toute observation ou signalement de faille de sécurité (responsible disclosure), écrire à pedro.berbel@dezvolta.org — réponse sous 48h ouvrées.