Audit technique du site
Cet audit est interne et automatisé : ce sont les outils de référence du métier, lancés sur le code du site, avec leurs résultats bruts publiés sans retouche. Ce n'est pas une certification délivrée par un organisme extérieur. Tout est reproductible : les scripts sont dans le dépôt public, et le fichier de résultats complet est téléchargeable plus bas.
0 défaut d'accessibilité WCAG 2.2 (niveaux A et AA) sur 13 pages, testées sur téléphone et sur ordinateur, aucune ne débordant en largeur.
Note d'accessibilité Lighthouse : 100 sur 100 sur chaque page.
8 contrôles de sécurité sur 9 tenus, 1 en défaut.
0 faille connue dans les 52 bibliothèques du serveur.
0 problème retenu par l'analyse statique du code (5 signalements, tous vérifiés).
626 tests automatiques réussis, 0 en échec.
Accessibilité
Chaque page est analysée par axe-core 4.13.0, le moteur utilisé par les navigateurs et la plupart des audits d'accessibilité, sur toutes les règles automatisables des WCAG 2.0, 2.1 et 2.2 (niveaux A et AA) et ses bonnes pratiques. Deux pages sont vues connecté, avec un compte de test. Nombre de défauts trouvés :
| Page | Téléphone (390 px) | Ordinateur (1366 px) |
|---|---|---|
| Accueil | 0 50 règles tenues | 0 50 règles tenues |
| Un bilan (portefeuille fictif) | 0 51 règles tenues | 0 51 règles tenues |
| Sécurité | 0 34 règles tenues | 0 34 règles tenues |
| Vos données | 0 34 règles tenues | 0 34 règles tenues |
| Questions fréquentes | 0 27 règles tenues | 0 27 règles tenues |
| Tarifs | 0 21 règles tenues | 0 21 règles tenues |
| Rapport sur devis | 0 37 règles tenues | 0 37 règles tenues |
| Se connecter | 0 30 règles tenues | 0 30 règles tenues |
| Créer un compte | 0 32 règles tenues | 0 32 règles tenues |
| Mentions légales | 0 31 règles tenues | 0 31 règles tenues |
| Cette page d'audit | 0 41 règles tenues | 0 41 règles tenues |
| Espace personnel (connecté) | 0 36 règles tenues | 0 36 règles tenues |
| Connexions (connecté) | 0 34 règles tenues | 0 34 règles tenues |
Performance, bonnes pratiques et référencement
Notes sur 100 de Lighthouse 13.5.0, l'outil de Google, au profil « téléphone » (processeur ralenti, réseau 4G lent simulé) puis « ordinateur ». 90 et plus en vert, 50 à 89 en orange, moins de 50 en rouge.
Sur téléphone
| Page | PerformancePerf. | AccessibilitéAccess. | Bonnes pratiquesPrat. | RéférencementRéf. |
|---|---|---|---|---|
| Accueil | 94 | 100 | 100 | 100 |
| Un bilan (portefeuille fictif) | 99 | 100 | 100 | non indexée* |
| Sécurité | 100 | 100 | 100 | 100 |
| Vos données | 100 | 100 | 100 | 100 |
| Questions fréquentes | 100 | 100 | 100 | 100 |
| Tarifs | 100 | 100 | 100 | 100 |
| Rapport sur devis | 100 | 100 | 100 | non indexée* |
| Se connecter | 100 | 100 | 100 | non indexée* |
| Créer un compte | 100 | 100 | 100 | non indexée* |
| Mentions légales | 100 | 100 | 100 | non indexée* |
| Cette page d'audit | 100 | 100 | 100 | 100 |
Sur ordinateur
| Page | PerformancePerf. | AccessibilitéAccess. | Bonnes pratiquesPrat. | RéférencementRéf. |
|---|---|---|---|---|
| Accueil | 98 | 100 | 100 | 100 |
| Un bilan (portefeuille fictif) | 100 | 100 | 100 | non indexée* |
| Sécurité | 100 | 100 | 100 | 100 |
| Vos données | 100 | 100 | 100 | 100 |
| Questions fréquentes | 100 | 100 | 100 | 100 |
| Tarifs | 100 | 100 | 100 | 100 |
| Rapport sur devis | 100 | 100 | 100 | non indexée* |
| Se connecter | 100 | 100 | 100 | non indexée* |
| Créer un compte | 100 | 100 | 100 | non indexée* |
| Mentions légales | 100 | 100 | 100 | non indexée* |
| Cette page d'audit | 100 | 100 | 100 | 100 |
Sécurité
8 contrôles sur 9 tenus. Chaque contrôle est lancé par audit/securite.py contre le site servi en local (même code, base jetable, portefeuille fictif) : les en-têtes et les cookies sont lus dans les réponses, chaque route qui écrit est appelée depuis un autre site, chaque page de compte est demandée sans session, chaque plafond est martelé jusqu'au refus. Les valeurs mesurées sont toutes dans le fichier JSON ; un contrôle en défaut est marqué ✗, jamais retiré.
En-têtes HTTP
Relevés sur 12 pages, dont la page de connexion de portefeuille. Vérifiable par vous : outils de développement du navigateur (F12), onglet Réseau, en-têtes de la réponse.
| Verdict | Contrôle | Valeur mesurée | Ce que cela prouve |
|---|---|---|---|
| ✓ | Content-Security-Policy : scripts du site seulement | default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'self' (sur 11 pages)Connexion de portefeuille : default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://*.walletconnect.com https://*.walletconnect.org https://api.web3modal.org https://*.web3modal.org https://imagedelivery.net; font-src 'self' data:; connect-src 'self' wss://*.walletconnect.com wss://*.walletconnect.org https://*.walletconnect.com https://*.walletconnect.org https://api.web3modal.org https://*.web3modal.org https://*.reown.com; frame-src https://verify.walletconnect.com https://verify.walletconnect.org https://secure.walletconnect.org https://secure-mobile.walletconnect.org https://secure-mobile.walletconnect.com; frame-ancestors 'none'; base-uri 'none'; form-action 'self' | Aucun script venu d'un autre site ni écrit dans la page ne peut s'exécuter : un contenu injecté (adresse, nom de jeton) ne devient pas du code. Les styles en ligne restent permis (le site en utilise). |
| ✓ | Strict-Transport-Security | max-age=31536000 (sur les 12 pages) | Une fois le site vu en HTTPS, le navigateur refuse d'y revenir en clair pendant un an. |
| ✓ | X-Content-Type-Options | nosniff (sur les 12 pages) | Le navigateur ne devine pas le type d'un fichier : un fichier envoyé comme image ne sera pas exécuté. |
| ✓ | Cadres interdits (frame-ancestors, X-Frame-Options) | DENY (sur les 12 pages) | Le site ne peut pas être affiché dans un cadre d'un autre site : pas de clic détourné (clickjacking). |
| ✓ | Referrer-Policy | same-origin (sur les 12 pages) | L'adresse de la page (qui peut contenir votre adresse de portefeuille) n'est pas transmise aux autres sites. |
| ✓ | Permissions-Policy | browsing-topics=(), interest-cohort=() (sur les 12 pages) | Pas de suivi publicitaire par le navigateur (Topics, FLoC). |
Cookies
Les trois cookies que le site pose, tels que le serveur les envoie :
| Verdict | Contrôle | Valeur mesurée | Ce que cela prouve |
|---|---|---|---|
| ✓ | Cookie du niveau choisi (bd_niveau) | bd_niveau=…; HttpOnly; Max-Age=31536000; Path=/; SameSite=lax; Secure | Votre choix débutant / intermédiaire / confirmé. HttpOnly : illisible par un script ; Secure : jamais envoyé en clair ; SameSite=Lax : pas envoyé avec un formulaire venu d'un autre site. |
| ✓ | Cookie des alertes lues (bd_alu) | bd_alu=…; HttpOnly; Max-Age=31536000; Path=/; SameSite=lax; Secure | Les empreintes des alertes déjà vues. HttpOnly : illisible par un script ; Secure : jamais envoyé en clair ; SameSite=Lax : pas envoyé avec un formulaire venu d'un autre site. |
| ✓ | Cookie de session (compte connecté) | pat_session=…; HttpOnly; Max-Age=43200; Path=/; SameSite=lax; Secure | Votre session, signée par le serveur. HttpOnly : illisible par un script ; Secure : jamais envoyé en clair ; SameSite=Lax : pas envoyé avec un formulaire venu d'un autre site. |
Formulaires et origine des requêtes
| Verdict | Contrôle | Valeur mesurée | Ce que cela prouve |
|---|---|---|---|
| ✓ | Écritures refusées depuis un autre site (CSRF) | 34 routes sur 34 refusent une requête venue de https://ailleurs.example (403) et l'acceptent depuis le site | Un autre site ne peut pas faire agir votre navigateur à votre insu : retirer une adresse, changer votre pseudo, supprimer votre compte, payer. Chaque route qui écrit est appelée deux fois, depuis une origine étrangère puis depuis le site, et les deux réponses sont comparées. |
Les 34 routes essayées
| Route | Depuis un autre site | Depuis le site | Mécanisme |
|---|---|---|---|
✓ POST /admin/bonus/{ident} | 403 | 403 | garde-fou (auth/protect.py), avant l'application |
✓ POST /admin/frequentation/import | 403 | 404 | garde-fou (auth/protect.py), avant l'application |
✓ POST /admin/frequentation/reconstituer | 403 | 404 | garde-fou (auth/protect.py), avant l'application |
✓ POST /admin/frequentation/systeme/importer | 403 | 404 | garde-fou (auth/protect.py), avant l'application |
✓ POST /alertes/abonner | 403 | 401 | garde-fou (auth/protect.py), avant l'application |
✓ POST /alertes/desabonner | 403 | 200 | garde-fou (auth/protect.py), avant l'application |
✓ POST /api/connections | 403 | 401 | garde-fou (auth/protect.py), avant l'application |
✓ POST /api/parcours | 403 | 204 | garde-fou (auth/protect.py), avant l'application |
✓ POST /api/wallet | 403 | 401 | garde-fou (auth/protect.py), avant l'application |
✓ POST /auth/wallet | 403 | 400 | garde-fou (auth/protect.py), avant l'application |
✓ POST /bilan/ajouter | 403 | 400 | garde-fou (auth/protect.py), avant l'application |
✓ POST /bilan/retirer | 403 | 400 | garde-fou (auth/protect.py), avant l'application |
✓ POST /billing/checkout | 403 | 401 | garde-fou (auth/protect.py), avant l'application |
✓ POST /billing/crypto | 403 | 401 | garde-fou (auth/protect.py), avant l'application |
✓ POST /billing/portal | 403 | 401 | garde-fou (auth/protect.py), avant l'application |
✓ POST /compte/mot-de-passe | 403 | 401 | garde-fou (auth/protect.py), avant l'application |
✓ POST /compte/pseudo | 403 | 401 | garde-fou (auth/protect.py), avant l'application |
✓ POST /compte/supprimer | 403 | 401 | garde-fou (auth/protect.py), avant l'application |
✓ POST /connexion | 403 | 400 | garde-fou (auth/protect.py), avant l'application |
✓ POST /contact | 403 | 400 | garde-fou (auth/protect.py), avant l'application |
✓ POST /declaration/{declaration_id}/acheter | 403 | 401 | garde-fou (auth/protect.py), avant l'application |
✓ POST /espace/adresse/retirer | 403 | 401 | garde-fou (auth/protect.py), avant l'application |
✓ POST /inscription | 403 | 400 | garde-fou (auth/protect.py), avant l'application |
✓ POST /logout | 403 | 303 | garde-fou (auth/protect.py), avant l'application |
✓ POST /mot-de-passe-oublie | 403 | 400 | garde-fou (auth/protect.py), avant l'application |
✓ POST /offre/majeur | 403 | 303 | garde-fou (auth/protect.py), avant l'application |
✓ POST /plateformes/connecter | 403 | 401 | garde-fou (auth/protect.py), avant l'application |
✓ POST /plateformes/importer | 403 | 401 | garde-fou (auth/protect.py), avant l'application |
✓ POST /plateformes/ledger | 403 | 401 | garde-fou (auth/protect.py), avant l'application |
✓ POST /plateformes/{ident}/retirer | 403 | 401 | garde-fou (auth/protect.py), avant l'application |
✓ POST /reinitialiser | 403 | 400 | garde-fou (auth/protect.py), avant l'application |
✓ POST /retour | 403 | 200 | garde-fou (auth/protect.py), avant l'application |
✓ POST /verifier/renvoyer | 403 | 303 | garde-fou (auth/protect.py), avant l'application |
✓ POST /webhooks/stripe | 400 | 400 | signature du webhook |
Accès aux pages de compte
| Verdict | Contrôle | Valeur mesurée | Ce que cela prouve |
|---|---|---|---|
| ✓ | Pages et actions de compte sans session | 35 routes sur 35 répondent 401, 403, 404 ou renvoient à la connexion | Espace personnel, plateformes, déclarations, paiement, administration : rien ne s'affiche ni ne s'exécute sans être connecté. Les pages d'administration répondent « introuvable » à tout autre compte. |
Les 35 routes essayées
| Route | Réponse sans session |
|---|---|
✓ GET /admin/bonus | 403 |
✓ GET /admin/contact | 404 |
✓ GET /admin/frequentation | 303 → /login |
✓ GET /admin/liquidite | 303 → /login |
✓ GET /admin/retours | 404 |
✓ GET /api/connections | 401 |
✓ GET /api/crypto/paiements | 401 |
✓ GET /billing | 303 → /connexion |
✓ GET /billing/crypto | 303 → /connexion |
✓ GET /compte/mot-de-passe | 303 → /connexion |
✓ GET /compte/pseudo | 303 → /connexion |
✓ GET /compte/supprimer | 303 → /connexion |
✓ GET /dashboard | 303 → /connexion |
✓ GET /declaration | 303 → /connexion |
✓ GET /declaration/{declaration_id} | 303 → /connexion |
✓ GET /declaration/{declaration_id}/telecharger | 303 → /connexion |
✓ GET /me | 303 → /connexion |
✓ POST /admin/bonus/{ident} | 403 |
✓ POST /admin/frequentation/import | 404 |
✓ POST /admin/frequentation/reconstituer | 404 |
✓ POST /admin/frequentation/systeme/importer | 404 |
✓ POST /api/connections | 401 |
✓ POST /billing/checkout | 401 |
✓ POST /billing/crypto | 401 |
✓ POST /billing/portal | 401 |
✓ POST /compte/mot-de-passe | 401 |
✓ POST /compte/pseudo | 401 |
✓ POST /compte/supprimer | 401 |
✓ POST /declaration/{declaration_id}/acheter | 401 |
✓ POST /espace/adresse/retirer | 401 |
✓ POST /offre/majeur | 303 → /connexion |
✓ POST /plateformes/connecter | 401 |
✓ POST /plateformes/importer | 401 |
✓ POST /plateformes/ledger | 401 |
✓ POST /plateformes/{ident}/retirer | 401 |
Plafonds de débit
Mesurés en martelant chaque route depuis une seule adresse IP, jusqu'au premier refus :
| Verdict | Contrôle | Valeur mesurée | Ce que cela prouve |
|---|---|---|---|
| ✓ | Plafond sur /connexion | plafond 20 par 15 minutes ; refus (429) mesuré à la requête n° 21 | Force brute sur les mots de passe : au-delà, le serveur répond 429 pendant un quart d'heure. |
| ✓ | Plafond sur /connexion (même e-mail) | plafond 5 par 15 minutes ; refus (429) mesuré à la requête n° 6 | Un même compte visé : cinq essais par quart d'heure et par adresse IP, puis verrouillage du compte après dix échecs. |
| ✓ | Plafond sur /auth/wallet | plafond 30 par 15 minutes ; refus (429) mesuré à la requête n° 31 | Connexions par portefeuille : même plafond que les formulaires de compte. |
| ✓ | Plafond sur /bilan | plafond 120 par minute ; refus (429) mesuré à la requête n° 121 | Un robot qui enchaîne les bilans est arrêté : le plafond global par identité tombe avant celui de la route (150 par heure), puis l'IP anonyme est limitée en bilans par heure et par jour. |
Secrets
| Verdict | Contrôle | Valeur mesurée | Ce que cela prouve |
|---|---|---|---|
| ✓ | Aucun secret dans le code | 118 fichiers parcourus, 8 motifs (clés Stripe, AWS, GitHub, clés privées, mots de passe en clair…) : 0 secret trouvé, 2 correspondances écartées après lecture ; 71 variables d'environnement lues | Clés d'API, mots de passe et secrets de session viennent de l'environnement du serveur, jamais du code publié : le dépôt public ne contient rien à voler. |
Bibliothèques
| Verdict | Contrôle | Valeur mesurée | Ce que cela prouve |
|---|---|---|---|
| ✓ | Aucune faille connue dans les bibliothèques | pip-audit (base PyPA / OSV) 2.10.1 : 52 bibliothèques, 0 faille | Chaque bibliothèque du serveur est comparée à la base des failles publiées (PyPA, OSV). |
| ✗ | Versions des bibliothèques figées | 0 figées (==), 20 avec une borne basse (>=) sur 20 : fastapi>=0.115, uvicorn[standard]>=0.30, httpx>=0.27, joserfc>=1.0, sqlalchemy>=2.0, itsdangerous>=2.2, … | Des versions figées garantissent que le serveur déployé est exactement celui audité. Avec une borne basse seulement, chaque déploiement peut installer une version plus récente que celle passée ici : point ouvert, à corriger. |
Analyse statique
| Verdict | Contrôle | Valeur mesurée | Ce que cela prouve |
|---|---|---|---|
| ✓ | Analyse statique du code (bandit) | bandit (sévérité moyenne et haute) 1.9.4, 27168 lignes : 5 signalements, haute : B413 ×2 ; moyenne : B310 ×1, B314 ×2 ; 0 retenu après vérification | Le code est passé au crible des motifs dangereux connus (injections, hachages faibles, désérialisation, XML). Chaque signalement de sévérité moyenne ou haute est lu ; ceux écartés le sont avec leur raison, ci-dessous. |
Ce que cet audit ne couvre pas
- Pas de test d'intrusion : personne n'a tenté d'attaquer le site en production. Les contrôles de sécurité vérifient que les protections annoncées sont en place ; ils ne cherchent pas de faille de logique métier.
- Le site est audité sur une copie locale du code publié, avec un portefeuille fictif : les temps de réponse réels dépendent aussi de l'hébergeur et des sources de données (blockchains, plateformes).
- La configuration de l'hébergeur (Render) et de la base de données n'est pas visible d'ici.
Refaire l'audit
Les scripts sont dans le dossier audit/ du dépôt public : npm install puis ./lancer.sh. Ils lancent le site en local, les mêmes outils dans les mêmes versions, et réécrivent le fichier de résultats.
Télécharger les résultats complets (JSON) : chaque note, chaque règle, chaque défaut avec l'élément concerné.