Aller au contenu

Audit technique du site

Audit du 30 septembre 2026 à 16 h 36 (UTC), version 0.6.8, code audité : 50e0aecc81

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 :

PageTéléphone (390 px)Ordinateur (1366 px)
Accueil0
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ées0
34 règles tenues
0
34 règles tenues
Questions fréquentes0
27 règles tenues
0
27 règles tenues
Tarifs0
21 règles tenues
0
21 règles tenues
Rapport sur devis0
37 règles tenues
0
37 règles tenues
Se connecter0
30 règles tenues
0
30 règles tenues
Créer un compte0
32 règles tenues
0
32 règles tenues
Mentions légales0
31 règles tenues
0
31 règles tenues
Cette page d'audit0
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

Un contrôle automatique ne remplace pas un test avec un lecteur d'écran : il couvre environ la moitié des critères WCAG. Les autres (sens des textes, ordre de lecture, usage réel au clavier) ont été revus à la main, sans garantie d'exhaustivité.

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

PagePerformancePerf.AccessibilitéAccess.Bonnes pratiquesPrat.RéférencementRéf.
Accueil94100100100
Un bilan (portefeuille fictif)99100100non indexée*
Sécurité100100100100
Vos données100100100100
Questions fréquentes100100100100
Tarifs100100100100
Rapport sur devis100100100non indexée*
Se connecter100100100non indexée*
Créer un compte100100100non indexée*
Mentions légales100100100non indexée*
Cette page d'audit100100100100

Sur ordinateur

PagePerformancePerf.AccessibilitéAccess.Bonnes pratiquesPrat.RéférencementRéf.
Accueil98100100100
Un bilan (portefeuille fictif)100100100non indexée*
Sécurité100100100100
Vos données100100100100
Questions fréquentes100100100100
Tarifs100100100100
Rapport sur devis100100100non indexée*
Se connecter100100100non indexée*
Créer un compte100100100non indexée*
Mentions légales100100100non indexée*
Cette page d'audit100100100100

* Ces pages sont volontairement exclues des moteurs de recherche (un bilan personnel, un formulaire, les pages de compte) : Lighthouse les note bas en référencement précisément pour cette raison.

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.

VerdictContrôleValeur mesuréeCe que cela prouve
✓Content-Security-Policy : scripts du site seulementdefault-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-Securitymax-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-Optionsnosniff (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-Policysame-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-Policybrowsing-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 :

VerdictContrôleValeur mesuréeCe que cela prouve
✓Cookie du niveau choisi (bd_niveau)bd_niveau=…; HttpOnly; Max-Age=31536000; Path=/; SameSite=lax; SecureVotre 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; SecureLes 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; SecureVotre 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

VerdictContrôleValeur mesuréeCe 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 siteUn 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
RouteDepuis un autre siteDepuis le siteMécanisme
✓ POST /admin/bonus/{ident}403403garde-fou (auth/protect.py), avant l'application
✓ POST /admin/frequentation/import403404garde-fou (auth/protect.py), avant l'application
✓ POST /admin/frequentation/reconstituer403404garde-fou (auth/protect.py), avant l'application
✓ POST /admin/frequentation/systeme/importer403404garde-fou (auth/protect.py), avant l'application
✓ POST /alertes/abonner403401garde-fou (auth/protect.py), avant l'application
✓ POST /alertes/desabonner403200garde-fou (auth/protect.py), avant l'application
✓ POST /api/connections403401garde-fou (auth/protect.py), avant l'application
✓ POST /api/parcours403204garde-fou (auth/protect.py), avant l'application
✓ POST /api/wallet403401garde-fou (auth/protect.py), avant l'application
✓ POST /auth/wallet403400garde-fou (auth/protect.py), avant l'application
✓ POST /bilan/ajouter403400garde-fou (auth/protect.py), avant l'application
✓ POST /bilan/retirer403400garde-fou (auth/protect.py), avant l'application
✓ POST /billing/checkout403401garde-fou (auth/protect.py), avant l'application
✓ POST /billing/crypto403401garde-fou (auth/protect.py), avant l'application
✓ POST /billing/portal403401garde-fou (auth/protect.py), avant l'application
✓ POST /compte/mot-de-passe403401garde-fou (auth/protect.py), avant l'application
✓ POST /compte/pseudo403401garde-fou (auth/protect.py), avant l'application
✓ POST /compte/supprimer403401garde-fou (auth/protect.py), avant l'application
✓ POST /connexion403400garde-fou (auth/protect.py), avant l'application
✓ POST /contact403400garde-fou (auth/protect.py), avant l'application
✓ POST /declaration/{declaration_id}/acheter403401garde-fou (auth/protect.py), avant l'application
✓ POST /espace/adresse/retirer403401garde-fou (auth/protect.py), avant l'application
✓ POST /inscription403400garde-fou (auth/protect.py), avant l'application
✓ POST /logout403303garde-fou (auth/protect.py), avant l'application
✓ POST /mot-de-passe-oublie403400garde-fou (auth/protect.py), avant l'application
✓ POST /offre/majeur403303garde-fou (auth/protect.py), avant l'application
✓ POST /plateformes/connecter403401garde-fou (auth/protect.py), avant l'application
✓ POST /plateformes/importer403401garde-fou (auth/protect.py), avant l'application
✓ POST /plateformes/ledger403401garde-fou (auth/protect.py), avant l'application
✓ POST /plateformes/{ident}/retirer403401garde-fou (auth/protect.py), avant l'application
✓ POST /reinitialiser403400garde-fou (auth/protect.py), avant l'application
✓ POST /retour403200garde-fou (auth/protect.py), avant l'application
✓ POST /verifier/renvoyer403303garde-fou (auth/protect.py), avant l'application
✓ POST /webhooks/stripe400400signature du webhook

Accès aux pages de compte

VerdictContrôleValeur mesuréeCe que cela prouve
✓Pages et actions de compte sans session35 routes sur 35 répondent 401, 403, 404 ou renvoient à la connexionEspace 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
RouteRéponse sans session
✓ GET /admin/bonus403
✓ GET /admin/contact404
✓ GET /admin/frequentation303 → /login
✓ GET /admin/liquidite303 → /login
✓ GET /admin/retours404
✓ GET /api/connections401
✓ GET /api/crypto/paiements401
✓ GET /billing303 → /connexion
✓ GET /billing/crypto303 → /connexion
✓ GET /compte/mot-de-passe303 → /connexion
✓ GET /compte/pseudo303 → /connexion
✓ GET /compte/supprimer303 → /connexion
✓ GET /dashboard303 → /connexion
✓ GET /declaration303 → /connexion
✓ GET /declaration/{declaration_id}303 → /connexion
✓ GET /declaration/{declaration_id}/telecharger303 → /connexion
✓ GET /me303 → /connexion
✓ POST /admin/bonus/{ident}403
✓ POST /admin/frequentation/import404
✓ POST /admin/frequentation/reconstituer404
✓ POST /admin/frequentation/systeme/importer404
✓ POST /api/connections401
✓ POST /billing/checkout401
✓ POST /billing/crypto401
✓ POST /billing/portal401
✓ POST /compte/mot-de-passe401
✓ POST /compte/pseudo401
✓ POST /compte/supprimer401
✓ POST /declaration/{declaration_id}/acheter401
✓ POST /espace/adresse/retirer401
✓ POST /offre/majeur303 → /connexion
✓ POST /plateformes/connecter401
✓ POST /plateformes/importer401
✓ POST /plateformes/ledger401
✓ POST /plateformes/{ident}/retirer401

Plafonds de débit

Mesurés en martelant chaque route depuis une seule adresse IP, jusqu'au premier refus :

VerdictContrôleValeur mesuréeCe que cela prouve
✓Plafond sur /connexionplafond 20 par 15 minutes ; refus (429) mesuré à la requête n° 21Force 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° 6Un 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/walletplafond 30 par 15 minutes ; refus (429) mesuré à la requête n° 31Connexions par portefeuille : même plafond que les formulaires de compte.
✓Plafond sur /bilanplafond 120 par minute ; refus (429) mesuré à la requête n° 121Un 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.

Plafond général : 120 requêtes par minute et par identité ; sans compte, 100 bilans par heure et 1000 par jour pour une IP, 10000 par jour pour tout le site. S'y ajoutent la détection de cadence de script et le bannissement temporaire (auth/protect.py).

Secrets

VerdictContrôleValeur mesuréeCe que cela prouve
✓Aucun secret dans le code118 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 luesClé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.
  • bilan/cex_api.py, ligne 51, clé privée PEM : -----BEGIN EC PRIVATE KEY, écarté : texte d'aide du formulaire Coinbase : le format de clé attendu, montré à l'utilisateur
  • web/cex.py, ligne 561, clé privée PEM : -----BEGIN EC PRIVATE KEY, écarté : libellé du champ « Clé privée » du formulaire Coinbase : le format attendu, pas une clé

Bibliothèques

VerdictContrôleValeur mesuréeCe que cela prouve
✓Aucune faille connue dans les bibliothèquespip-audit (base PyPA / OSV) 2.10.1 : 52 bibliothèques, 0 failleChaque bibliothèque du serveur est comparée à la base des failles publiées (PyPA, OSV).
✗Versions des bibliothèques figées0 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

VerdictContrôleValeur mesuréeCe 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érificationLe 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.
  • B413 (2), pycryptodome (paquet maintenu, API compatible) est pris pour l'ancien pyCrypto : seul Keccak-256 y est utilisé, pour vérifier les adresses Ethereum.
  • B310 (1), urlopen n'ouvre que des adresses https fixées dans le code (explorateurs Bitcoin, cours) : aucun schéma venu de l'utilisateur.
  • B314 (2), flux RSS d'actualités publics lus avec la bibliothèque standard, dont l'analyseur (expat ≥ 2.4) bloque l'expansion d'entités en chaîne ; aucune entité externe n'est résolue.

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é.

Bilan Crypto v0.8