Le miroir GitHub audite en premiere passe etait en retard de 16 commits
sur git.immortal.host/clubesportsudes/team-tryouts. L'audit est rebase
sur immortal/main @ bb0bc1c et l'ensemble des constats reverifie.
Resolus par l'equipe (archives dans 00-provenance.md) :
- seed automatique en production supprime
- print du token Discord supprime
- proxy_pass nginx corrige vers 127.0.0.1
- dossier supporting_scrits renomme
Nouveaux constats :
- SEC-20 flux OAuth2 Discord sans parametre state (CSRF de liaison)
- SEC-21 le deploiement SFTP pousse .git/ sur le serveur
- SEC-22 clear_db.py destructif sans garde-fou, admin/password en dur
- MNT-16 discord_pending.json versionne
Requalifies :
- SEC-01 secrets retires du fichier mais toujours dans l'historique des
deux depots, et dans le HEAD du miroir GitHub -> revocation requise
- SEC-05 trusted_proxy='*' + bind 0.0.0.0 rend X-Forwarded-For usurpable,
ce qui ouvre le rate limiting au lieu de le corriger
- SEC-07 l'OAuth2 ajoute ne contraint pas l'identite : le discord_user_id
transite par un champ cache du formulaire
- MNT-05 psycopg[binary] non epingle, incompatible avec les URI
postgresql:// que SQLAlchemy resout vers psycopg2
48 constats. Aucune modification du code applicatif.
Co-Authored-By: Claude Opus 5 <[email protected]>
8.2 KiB
0 — Provenance et écart entre les dépôts
Deux dépôts, un seul fait autorité
| Dépôt | Rôle | HEAD main |
|---|---|---|
git.immortal.host/clubesportsudes/team-tryouts |
Référence — c'est celui-ci qui est audité | bb0bc1c |
github.com/cedrick2711/team-tryouts |
Miroir, en retard de 16 commits | 08f02f7 |
Les deux partagent un ancêtre commun (d6fe505). Le miroir GitHub porte en outre 2 commits qui ne sont pas sur le dépôt de référence (0d5a408 fix registration et son merge 08f02f7) ; le correctif correspondant existe sur immortal sous la forme du commit 7d30aff Corriger erreur d'enregistrement.
La première passe de cet audit a été menée sur le miroir GitHub, avant que la bonne source ne soit connue. L'ensemble des constats a été revérifié contre immortal/main. Le présent document liste ce qui a changé.
Les 16 commits d'écart
bb0bc1c ajout de plateforme de base pour les url TRN
f89e4de régler problème avec mise a jour des status de message du discord bot
fcf10bc bug fix: Manager ne pouvait pas voir les tryouts. probleme avec discord bot
68e3da6 fix probleme avec dispos
d25b35c régler erreur 500 sur changement de role par admin
aeba4d7 régler problème de changement de rôle
7bb8022 added a clear_db to start fresh with only an admin
0dd4ecd Erreur de frappe dans un des dossiers
0f7788e Changement du layout de la page d'enregistrement
47d5ec4 added discord oauth2 to get basic user info to complete profile when registering
10af8c0 small change for prod
7d30aff Corriger erreur d'enregistrement
4097416 Update wsgi.py
056cea0 Update .gitea/workflows/git-to-ptero.yaml
d3b5700 Add .gitea/workflows/git-to-ptero.yaml
fd258de Update app/.env.exemple
Constats de la première passe devenus caducs
Quatre constats ont été résolus par l'équipe entre les deux points de l'historique. Ils sont conservés ici pour mémoire, et retirés du décompte des documents 01 à 03.
✅ Seed automatique en production — résolu
Le bloc de app/app.py qui déclenchait le seed quand la table users était vide a été supprimé (commit 7bb8022), et app/supporting_scrits/seed.py (460 lignes, comptes manager1, coach1, scout1… tous avec le mot de passe password) a été supprimé.
with app.app_context():
import app.models as models
- from app.models import User
db.create_all()
-
- # Seed database if empty
- if User.query.count() == 0:
- from app.supporting_scrits.seed import seed_database
- seed_database()
Un déploiement neuf ne crée donc plus de comptes par défaut.
Reste à traiter. Le script de remplacement
clear_db.pycode toujours en duradmin/password, et il est bien plus dangereux que l'ancien seed sur un autre plan. Voir SEC-22.À vérifier en production, indépendamment du code : les comptes créés par l'ancien seed (
admin,manager1,manager2,coach1,coach2,coach3,scout1) peuvent toujours exister en base avec le mot de passepassword. La suppression du script ne supprime pas les comptes qu'il a créés.
✅ Token Discord imprimé sur stdout — résolu
La ligne print(DISCORD_BOT_TOKEN or 'FAILED TO PRINT BOT TOKEN') de app/discord_bot.py:25 a été supprimée. Le module ne comporte plus aucun print().
✅ proxy_pass vers 0.0.0.0 — résolu
app/nginx.conf:122 : proxy_pass http://0.0.0.0:5000; → proxy_pass http://127.0.0.1:5000;
Reste à traiter. L'incohérence de ports subsiste :
wsgi.py:24utilise toujoursPORTpar défaut à 10000 alors que nginx envoie vers 5000 et que le docstring du même fichier annonce 5000. Voir STD-06.
✅ Faute de frappe supporting_scrits — résolu
Le dossier a été renommé app/supporting_scripts/ (commit 0dd4ecd).
Effet de bord non traité. Le job CI « Security Scan » invoquait déjà un chemin erroné (
python security_scan.pyà la racine) ; le renommage l'éloigne encore. Voir MNT-04.
Constats aggravés ou requalifiés
⚠️ Secrets exposés — le nettoyage n'a pas révoqué
app/.env.exemple a été nettoyé (commit fd258de) : les valeurs réelles ont été remplacées par des marqueurs (flask_app_secret_key, my_discord_bot_token, URI_vers_db_posgres).
Mais les secrets restent intégralement récupérables. Vérification par recherche dans l'historique complet des deux dépôts :
$ git log --all --oneline -S "MTUyNzY3ODU3NjUyNTA1NDEyNQ"
fd258de Update app/.env.exemple ← retrait
2d3721b Ajout d'un .env.exemple pour simplifier la collaboration ← introduction
Le blob contenant le token Discord, le mot de passe PostgreSQL et la SECRET_KEY est toujours atteignable par git show 2d3721b:app/.env.exemple sur les deux dépôts. Il est de surcroît toujours dans le HEAD du miroir GitHub, donc visible par simple navigation dans l'interface web.
Le constat reste donc critique et sa correction inchangée : révoquer, puis purger. Voir SEC-01.
⚠️ En-têtes de proxy — le correctif a inversé le risque
wsgi.py a reçu un bloc de configuration proxy (commit 4097416) :
trusted_proxy='*',
trusted_proxy_count=1,
trusted_proxy_headers={'x-forwarded-for', 'x-forwarded-proto'},
clear_untrusted_proxy_headers=True
L'intention est bonne, et clear_untrusted_proxy_headers=True est le bon réflexe. Mais trusted_proxy='*' signifie « faire confiance aux en-têtes de proxy quelle qu'en soit la provenance », et HOST vaut toujours 0.0.0.0 par défaut (wsgi.py:26).
Avant ce changement, request.remote_addr valait toujours l'IP de nginx : le rate limiting était appliqué à un seau global — gênant, mais fermé. Désormais, un attaquant qui atteint directement le port applicatif contrôle X-Forwarded-For et peut donc présenter une IP différente à chaque requête, ce qui neutralise le rate limiting et le suivi de tentatives par IP.
Le constat change de nature et reste élevé. Voir SEC-05.
⚠️ Identifiant Discord — la vérification ajoutée n'en est pas une
Un flux OAuth2 Discord complet a été ajouté à l'inscription (commit 47d5ec4, auth.py:326-456). Il obtient de Discord un identifiant authentifié et le place en session.
Mais cet identifiant est ensuite réinjecté dans le formulaire d'inscription par un champ caché :
<!-- register.html:61 -->
<input type="hidden" name="discord_user_id" value="{{ discord_data.id }}">
et c'est cette valeur — passée par le client — qui est enregistrée (auth.py:253, :290). Un utilisateur peut la modifier avant envoi, ou poster directement le formulaire sans jamais passer par Discord.
RegisterSchema valide désormais le format de discord_user_id (17-20 chiffres, validators.py:211-215), ce qui est un progrès, mais ne prouve rien sur la propriété du compte.
La conséquence pratique est inchangée par rapport à la première passe — et le risque de fausse assurance est nouveau, puisque le flux paraît vérifié. Voir SEC-07.
⚠️ Limite d'inscription desserrée
auth.py:190 : @limiter.limit("3 per hour") → @limiter.limit("20 per hour").
Le CAPTCHA arithmétique étant trivial (SEC-13), le rate limiting était la protection réellement efficace contre la création automatisée de comptes. Passer à 20/heure la divise par un facteur ~7, et SEC-05 la rend contournable.
Éléments nouveaux ajoutés à l'audit
| Élément | Constat |
|---|---|
auth.py:326-456 — flux OAuth2 Discord |
SEC-20 absence de paramètre state · SEC-07 identité non contraignante |
.gitea/workflows/git-to-ptero.yaml |
SEC-21 déploiement de .git/ par SFTP |
clear_db.py |
SEC-22 script destructif sans garde-fou |
migrations/add_tryout_coaches.py |
STD-01 migration manuelle hors outillage |
discord_pending.json |
MNT-16 état d'exécution versionné |