Résumé

  • Le client PAI publié par LACNIC conserve les données d’autorisation dans un cache de deux minutes. Un échec de décodage relevant du traitement prévu peut relancer ce délai sans renouveler les données.
  • Le test fourni attend explicitement ce maintien après un JSON malformé. Une nouvelle réponse négative correctement décodée remplace, au contraire, l’entrée ancienne.
  • L’inspection ne démontre ni utilisation en production, ni maintien d’un jeton révoqué, ni contournement d’autorisation. Elle établit un décalage possible entre deux horloges.

Une interruption évitée, une date perdue

Personne ne souhaite qu’un problème passager chez un fournisseur d’autorisation interrompe immédiatement toutes les sessions encore légitimes. Une application qui conserve un résultat récent peut traverser cette panne sans transformer la dépendance défaillante en arrêt général. La question intéressante commence après cette décision raisonnable : que signifie désormais « récent » ?

Une réponse reçue auparavant ne devient pas une réponse nouvelle parce que l’application vient de la réutiliser. On peut pourtant donner à son contenant une nouvelle date. À partir de là, un cache âgé de quelques secondes peut transporter une décision nettement plus ancienne. Pour le lecteur du résultat, la différence disparaît si aucune autre date ne l’accompagne.

Le dépôt public pai-auth-ws-client de LACNIC permet d’observer précisément ce mécanisme. Cette analyse porte sur le commit b85523718bfdbd834c85add8e06c3b4a1b813e59, présent sur la branche principale observée le 14 septembre 2026. Le README présente un client Java pour les fonctions d’authentification, d’autorisation et de session de PAI. Il ne donne pas l’inventaire des installations actives. Un dépôt consultable n’est pas une preuve que tous les services associés à LACNIC exécutent ce chemin.

Le renouvellement porte sur le contenant

Dans PortalWSClient, une ConcurrentHashMap statique garde les résultats dans le processus. Sa clé est le jeton fourni à la méthode. CACHE_DURATION_MS fixe deux minutes de durée. Chaque entrée associe un objet TokenData à un horodatage modifiable ; l’expiration dépend du temps courant et de cette date.

Tant que l’entrée n’est pas expirée, la méthode retourne les données déjà conservées. Elle n’obtient pas alors une nouvelle réponse de /authorization. C’est un fonctionnement de cache ordinaire, avec les économies d’appels et la réduction des interruptions qui le justifient. Il ne faut pas transformer cette seule économie en accusation de défaut d’autorisation.

Lorsque l’entrée expire, elle est retirée de la table. La méthode conserve néanmoins une référence locale à cette entrée pendant sa tentative de rafraîchissement. Le retrait de la table partagée et la disparition de l’objet sont donc deux choses différentes. La référence conservée rend possible le retour à l’ancien résultat.

Si le remplacement est correctement décodé, le client crée une nouvelle entrée avec les nouvelles données. Rien dans cette branche ne réserve ce traitement aux réponses positives. Un refus lisible suit le chemin du résultat nouveau. Présenter ce code comme une préférence systématique pour une autorisation passée contre un refus actuel serait inexact.

La prolongation se trouve dans le traitement d’IOException. Lorsqu’une ancienne entrée reste disponible, cached.extend() lui attribue l’heure courante, la remet dans la table et permet de retourner les mêmes données. Le client n’a pas obtenu, par cette opération, une nouvelle décision décodée. Il a rouvert la fenêtre de validité du cache.

Cette branche peut se répéter lorsque les conditions correspondantes se reproduisent. Dans cette classe, aucune date immuable ne conserve le début de l’autorisation décodée initiale ; aucun plafond absolu ne se mesure depuis cette réponse ; aucun compteur ne limite les prolongations. Cette description du code ne dit pas qu’une application utilisatrice n’a pas ajouté ses propres garde-fous.

Le test défend la continuité

Le test fourni, testGetTokenDataReturnsCachedDataWhenRefreshFails, est particulièrement instructif. Une première réponse HTTP simulée donne des données d’authentification positives. Le test place ensuite artificiellement l’horodatage cinq minutes dans le passé. La réponse suivante contient un JSON malformé. Les assertions attendent le même objet authentifié et deux exécutions HTTP simulées.

Les cinq minutes sont une manipulation du test, pas une durée mesurée sur un service réel. Le test a été lu, non exécuté pour cette enquête. Aucun point d’accès de production n’a été sollicité et aucun véritable jeton n’a été utilisé. Mais ce scénario suffit à montrer que le maintien recherché n’est pas une simple coïncidence de lecture : le résultat ancien est expressément attendu.

Cette intention mérite d’être prise au sérieux. La perte d’une réponse lisible n’implique pas que tous les droits viennent d’être retirés. Une période de grâce peut protéger le travail légitime. Son coût apparaît si personne ne peut dire quand a commencé la dernière réponse fiable, ou quelles actions restent acceptables pendant cette période.

Le code comporte aussi un avertissement sur l’utilisation temporaire d’un cache prolongé. Il serait donc faux de décrire la prolongation comme entièrement dépourvue de signal. Des équipes peuvent la surveiller dans les journaux. L’avertissement ne fournit cependant pas, dans l’objet retourné à l’appelant, l’âge de la décision conservée.

Ce que le résultat ne raconte pas

La classe TokenData contient notamment l’état d’authentification, le jeton, les rôles, un champ d’erreur et ipAllowed. Elle ne transporte pas de date de dernière réponse réussie, d’indication de fraîcheur ou de marqueur de retour dégradé. À elle seule, elle ne permet pas de distinguer une décision nouvelle d’une ancienne décision dont le cache vient d’être prolongé.

L’application peut conserver ces informations ailleurs. Elle peut contrôler l’expiration cryptographique du jeton, consulter de nouveau les droits avant une écriture sensible ou appliquer des restrictions d’adresse IP. Ces protections éventuelles ne sont ni démontrées ni exclues par cette inspection. La présence d’un jeton ou d’un booléen positif ne les remplace pas.

Il faut en particulier séparer la durée propre du jeton, la fenêtre courante du cache et l’actualité des rôles chez l’émetteur. Un changement de rôle pourrait suivre son propre calendrier ; le délai du cache ne documente pas ce calendrier. Confondre ces trois propositions donnerait au code une portée qu’il n’a pas.

Une autre réserve concerne la définition d’une réponse fiable. PortalHttpClient construit le client avec TrustAllStrategy et NoopHostnameVerifier. Dans cette implémentation, le fait qu’une réponse provienne d’une URL HTTPS et soit décodable ne prouve donc pas à lui seul une vérification cryptographique de l’identité de l’émetteur par ce composant. Ce constat distinct n’établit ni interception ni configuration TLS d’un système exploité. Une politique de fraîcheur devrait néanmoins préciser quelle réponse authentifiée remet son horloge à zéro.

Un mauvais document n’est pas toute panne

Le scénario documenté concerne une réponse malformée et le traitement qui peut intercepter son erreur de lecture. La méthode readUrlToken attrape plus largement des exceptions et peut retourner une valeur nulle ; le traitement extérieur porte, lui, sur IOException. On ne doit pas assimiler sans vérification une absence de contenu, une erreur de transport et un JSON illisible.

Dire « le client continue toujours si le service tombe » exagérerait donc sa garantie de continuité. Dire « toute panne maintient des droits révoqués » exagérerait le risque observé. Dans les deux cas, on remplacerait un chemin de code identifié par une affirmation générale sur des applications et des environnements non examinés.

La conclusion étayée est plus étroite : dans le cas de réponse malformée décrit par le test, le résultat ancien est retourné et son horodatage de cache renouvelé. Cette classe ne conserve pas de plafond fondé sur l’âge initial pour des répétitions de cette branche. Les effets réels dépendent de l’installation, des règles de l’émetteur et des vérifications supplémentaires de l’appelant.

Donner une origine à la période de grâce

Un contrat plus lisible garderait deux dates distinctes : la dernière réponse d’autorisation authentifiée et correctement décodée, puis la dernière modification du cache. Réutiliser l’entrée ne changerait que la seconde. L’appelant pourrait recevoir un état explicite — frais, en période de grâce, indisponible — plutôt qu’un résultat positif dont l’histoire est inconnue.

La grâce aurait alors une fin absolue, calculée depuis la réponse fiable, non depuis le dernier échec. Elle pourrait également dépendre de l’opération. Une consultation réversible ne pose pas nécessairement les mêmes exigences qu’un changement d’administrateur ou une instruction irréversible. Ce sont des classes de décisions proposées pour l’analyse, pas des fonctions de LACNIC dont l’utilisation de ce client serait établie.

La surveillance pourrait rapprocher avertissements, âge du résultat et mode dégradé sans enregistrer les jetons bruts. Une nouvelle réponse fiable mettrait fin à la grâce ; un refus frais resterait un refus. Ce sont des recommandations éditoriales, non un engagement annoncé par LACNIC. L’objectif n’est pas d’interdire les caches, mais d’éviter qu’un contenant rajeuni soit pris pour une autorisation réaccordée.

Sources

Les cinq sources primaires liées ci-dessus sont le README, PortalWSClient, TokenData, PortalWSClientTest et PortalHttpClient, au même commit. Elles documentent une implémentation publiée et un test simulé. Elles ne démontrent aucune exploitation en production, compromission, opération non autorisée ou persistance d’accès après révocation.