Visitar URL original
Audit final de l’app : clients customs et protection anti-abus · Issue #4791 · Crownicles/Crownicles · GitHub
Skip to content

Audit final de l’app : clients customs et protection anti-abus #4791

Open

Description

@BastLast

Contexte

Avant la publication de l’application, effectuer une passe dédiée sur le risque des clients customs et des forks open source.

Le client officiel est et doit rester open source. Un fork peut donc modifier l’interface, automatiser des actions ou embarquer un token compromis. Le serveur officiel ne doit jamais faire confiance au binaire utilisé : il doit vérifier l’identité, les droits, l’état du joueur et chaque mutation côté serveur.

Cette issue doit être traitée à la fin de la milestone App, après l’implémentation des flux, afin d’auditer le système réel plutôt qu’une architecture théorique.

Périmètre

Inclus

  • Écrire un modèle de menace couvrant les forks custom, l’automatisation, le rejeu de paquets, les tokens copiés ou exposés et les abus de l’économie ;
  • Vérifier qu’aucun secret serveur, client_secret, token permanent ou identifiant sensible n’est embarqué dans App, les builds mobiles ou les builds desktop ;
  • Auditer le cycle de vie des tokens Keycloak : durée, rotation, révocation, expiration, issuer, audience et séparation des permissions ;
  • Sécuriser l’ouverture WebSocket et vérifier qu’un access token n’est pas exposé dans les URL ou les logs ;
  • Valider les paquets entrants à la frontière RestWs : allowlist, schémas runtime, tailles maximales, types, valeurs autorisées et champs inattendus ;
  • Ajouter ou vérifier les limites par compte, IP et connexion : fréquence, taille des messages, actions simultanées et comportement abusif ;
  • Vérifier la protection contre le rejeu et la double exécution des actions à effet de bord ;
  • Vérifier que Core recalcule et contrôle toujours les coûts, récompenses, délais, propriétaires, collecteurs, inventaires, cibles et permissions ;
  • Supprimer ou masquer les tokens et données sensibles dans les logs de production ;
  • Tester le serveur avec des paquets fabriqués à la main et un client minimal non issu du code de l’application ;
  • Documenter la politique des clients alternatifs, des forks, de la marque Crownicles et des obligations AGPL ;
  • Vérifier que les protections retenues restent compatibles avec F-Droid, les builds communautaires et les clients macOS/Linux/Windows.

Canaris de protocole et signaux d’abus

Mettre en place des signaux de détection faciles à observer dans les logs, mais sans transformer le protocole en mécanisme de sécurité par obscurité :

  • journaliser de façon structurée les paquets inconnus, les champs inattendus, les enums impossibles, les index invalides, les identifiants de requête rejoués et les actions incompatibles avec l’état du joueur ;
  • détecter les séquences impossibles, les cadences anormales, les rafales de requêtes et les réponses systématiquement trop rapides pour le parcours normal ;
  • ajouter si nécessaire un handshake ou une négociation de version/capacités explicite, avec un signal lorsqu’un client contourne ou falsifie le contrat public ;
  • envisager des canaris purement observables et sans effet de jeu, par exemple des paquets ou capacités non documentés qui doivent être rejetés et journalisés, sans fausse récompense, faux coût, perte de ressource ou piège pouvant affecter un joueur ;
  • produire un événement de sécurité exploitable par le monitoring avec le compte, la connexion, le type de signal, la version du protocole et le contexte minimal nécessaire, sans token ni secret dans les données ;
  • conserver les signaux avec une durée de rétention et un accès documentés, en évitant de journaliser inutilement des données personnelles ;
  • ne jamais bannir sur un seul signal : combiner plusieurs signaux indépendants avec l’impact observé, prévoir les faux positifs des anciennes versions, des forks légitimes, de l’accessibilité et des problèmes réseau, puis appliquer une restriction temporaire ou une revue manuelle avant un bannissement définitif ;
  • tester ces canaris avec l’app officielle, un client minimal conforme, un client volontairement malformé et un scénario d’automatisation afin de mesurer le bruit et la valeur réelle de chaque signal.

Ces canaris ne pourront pas identifier un client custom qui reproduit parfaitement le protocole public. Leur but est de faire remonter rapidement les clients mal écrits ou abusifs, pas de prouver quel binaire est officiel.

Hors périmètre

  • Empêcher techniquement tous les forks ou tous les clients customs ;
  • Ajouter une clé secrète dans l’application, de l’obfuscation ou une détection basée uniquement sur le User-Agent ;
  • Rendre obligatoire App Attest, Play Integrity ou une autre preuve du binaire officiel ;
  • Réécrire le protocole ou déplacer les règles de jeu dans l’application ;
  • Introduire des pièges cachés qui modifient l’économie, faussent l’interface, consomment une ressource ou peuvent bannir un joueur sans preuve indépendante ;
  • Traiter les règles propres à Apple, F-Droid ou Steam déjà couvertes par leurs issues de publication, sauf pour vérifier leur compatibilité avec cet audit.

Terminé quand

  • Le modèle de menace, les risques acceptés et les protections retenues sont documentés ;
  • Un client custom peut se connecter uniquement avec un compte valide et ne peut agir qu’au nom de ce compte ;
  • Un paquet forgé ne peut pas modifier un prix, une récompense, une durée, une propriété ou une permission validée par Core ;
  • Un token copié peut être révoqué et n’apparaît ni dans les logs ni dans les URL de production ;
  • Les paquets invalides, trop volumineux, trop fréquents ou rejoués sont rejetés sans crash ni effet de bord ;
  • Les canaris de protocole sont non destructifs, documentés, testés et envoyés vers un événement de monitoring structuré ;
  • Chaque signal possède un niveau de confiance, une procédure de corrélation et une stratégie de faux positifs ;
  • Aucun bannissement automatique définitif ne repose sur un seul signal ;
  • Les limites d’abus et la procédure de sanction ou de révocation sont définies ;
  • Les tests couvrent au minimum l’usurpation de joueur, le rejeu, la double dépense, la double récompense, le spam, les paquets malformés et les faux positifs d’un client conforme ;
  • La documentation explique clairement ce qui est autorisé pour les forks et ce qui constitue un abus sur le serveur officiel ;
  • L’audit ne bloque pas les builds F-Droid, les clients desktop ou les forks open source légitimes ;
  • Les contrôles de sécurité et les tests ciblés passent dans les services concernés.

Dépend de

#4788, #4756, #4745, #4746, #4758

Activity

  1. added this to the App milestone on Aug 17, 2026
  2. self-assigned this
    on Aug 17, 2026
  3. added
    deployementtask related to deployement or tests
    legalLegal related stuff (tos/ privacypolicy...)
    appChanges needed specifically for the app
    testChecking that something works as intended
    on Aug 17, 2026
  4. BastLast commented on Aug 20, 2026

    @BastLast
    MemberAuthor

    Décision concernant les anciennes versions et les clients customs

    Le serveur officiel ne doit pas faire confiance au binaire utilisé, mais il doit pouvoir refuser les clients devenus incompatibles. Le contrôle de version sera placé dans RestWs, après authentification Keycloak et avant tout envoi MQTT vers Core.

    Le client devra déclarer au handshake ou à l’ouverture WebSocket : plateforme, version native, numéro de build, runtimeVersion et version de protocole. RestWs comparera ces informations à une politique configurable : version minimale supportée, build minimal, runtime accepté, date d’effet et liens vers les stores.

    Une ancienne version recevra une réponse explicite APP_UPDATE_REQUIRED, sans paquet de jeu et sans accès à Core. Ce mécanisme n’est pas une preuve qu’un binaire officiel est utilisé : un fork peut reproduire le protocole. La sécurité réelle reste l’authentification, la validation des paquets, les limites d’abus et les règles recalculées par Core.

    Contraintes à auditer

    • ne jamais considérer une clé secrète embarquée dans l’app comme une preuve d’authenticité ;
    • ne pas exposer de token ou de secret dans les logs ;
    • permettre les forks et builds communautaires compatibles lorsque la politique de sécurité l’autorise ;
    • traiter séparément une version obsolète, un runtime incompatible et un paquet malformé ;
    • journaliser le refus avec un identifiant de compte et des métadonnées minimales, sans secret ;
    • prévoir une période de grâce pour une date de blocage planifiée et un mode d’urgence pour une faille critique ;
    • tester qu’un client conforme est refusé proprement lorsqu’il est sous la version minimale, avant tout accès à Core.

    La publication à une date choisie par Apple ou Google ne garantit pas l’installation simultanée de tous les utilisateurs. Le serveur doit donc accepter une transition entre plusieurs versions compatibles et ne bloquer qu’à la date d’effet explicitement configurée.

  5. added theissue type on Aug 20, 2026
  6. BastLast commented on Sep 22, 2026

    @BastLast
    MemberAuthor

    Une partie du périmètre « cycle de vie des jetons Keycloak » a été traitée le 22/09, à reprendre telle quelle lors de l'audit final plutôt qu'à refaire.

    Plus aucun client n'accepte le grant mot de passe. Il était actif sur crownicles-app — le client public de l'app — ainsi que sur admin-cli, discord et restWs. N'importe qui pouvait donc éprouver des mots de passe par appel direct au point de terminaison, sans jamais voir le formulaire. Les deux routes qui l'utilisaient (POST /login, POST /refreshToken) n'étaient appelées par personne : l'app parle à Keycloak en PKCE, y compris pour le rafraîchissement. Elles sont supprimées, avec KeycloakUtils.loginUser, refreshUserToken et checkUserAccessToken. L'Admin API continue de fonctionner : elle utilisait déjà client_credentials.

    ⚠️ Piège pour l'audit : keycloak/realm.json indiquait déjà directAccessGrantsEnabled: false pour crownicles-app, alors que le realm réel était à true. Un ré-import depuis un export ramène ces valeurs. Vérifier le realm en fonctionnement, pas le fichier versionné.

    Autres points fermés

    • crownicles-app n'accepte plus crownicles://* mais crownicles://auth* : un schéma personnalisé n'est pas exclusif, une autre application peut l'enregistrer et intercepter le code. (non testé sur appareil)
    • passwordPolicy était vide : un mot de passe d'un caractère passait. Désormais 10 caractères minimum, refus du nom d'utilisateur et de l'adresse.
    • failureFactor 30 → 10.
    • Image Keycloak épinglée à 26.7.4 au lieu de latest.
    • Aucun secret embarqué dans le thème : Inter est servie localement plutôt que par Google Fonts.

    Restant sur ce périmètre : modèle de menace, validation des paquets à la frontière RestWs, limites par compte, exposition du jeton à l'ouverture WebSocket.

  7. BastLast commented on Sep 22, 2026

    @BastLast
    MemberAuthor

    Réserve levée : le resserrage de crownicles-app.redirectUris (crownicles://* → crownicles://auth*, exp://*/--/auth*, http://localhost:8081/auth*) a été testé sur iPhone — la connexion Discord aboutit et le retour d'authentification est reçu normalement.

    Ce point du périmètre est donc clos et vérifié sur appareil, plus seulement sur le papier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

appChanges needed specifically for the appdeployementtask related to deployement or testslegalLegal related stuff (tos/ privacypolicy...)testChecking that something works as intended

Type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions