PostgreSQLLa base de données la plus sophistiquée au monde.
Documentation PostgreSQL 18 beta 2 » Programmation serveur » Modules de validation OAuth » Concevoir un module de validation de manière sécurisée

50.1. Concevoir un module de validation de manière sécurisée #

Avertissement

Lire et comprendre entièrement cette section avant d'implémenter un module de validation est crucial. Un validateur défaillant est potentiellement pire qu'une absence totale d'authentification, car il donne une fausse impression de sécurité et peut être utilisé dans le cadre d'attaques ciblant d'autres composants de l'écosystème OAuth.

50.1.1. Responsabilités du validateur #

Bien que les modules puissent adopter des approches très différentes pour valider les jetons, une implémentation doit généralement effectuer trois actions distinctes :

Valider le jeton

Le validateur doit d'abord s'assurer que le jeton présenté est bien un jeton Bearer valide pour l'authentification du client. La manière exacte dépend du fournisseur, mais elle implique généralement soit des opérations cryptographiques pour prouver que le jeton a été émis par une entité de confiance (validation hors ligne), soit la présentation du jeton à cette entité afin qu'elle effectue la validation à sa place (validation en ligne).

La validation en ligne, généralement mise en œuvre via OAuth Token Introspection, demande moins d'étapes de la part du module et permet la révocation centralisée des jetons en cas de vol ou de mauvaise attribution. Cependant, elle nécessite au minimum un appel réseau par tentative d'authentification (tous devant être réalisés dans le délai défini par authentication_timeout). De plus, certains fournisseurs n'exposent pas de point d'introspection pour les serveurs de ressources externes.

La validation hors ligne est beaucoup plus complexe, nécessitant souvent que le validateur maintienne une liste de clés de signature de confiance et vérifie la signature cryptographique du jeton ainsi que son contenu. L'implémentation doit respecter à la lettre les directives du fournisseur, y compris la vérification de l'émetteur (« d'où vient ce jeton ? »), du destinataire (« pour qui est ce jeton ? ») et de la période de validité (« quand ce jeton est-il utilisable ? »). Comme il n'y a pas de communication avec le fournisseur, la révocation centralisée n'est pas possible ; les validateurs hors ligne peuvent vouloir restreindre la durée maximale de validité d'un jeton.

Si le jeton ne peut pas être validé, le module doit immédiatement échouer. Toute tentative d'authentification/autorisation est inutile si le jeton Bearer n'a pas été émis par une entité de confiance.

Autoriser le client

Ensuite, le validateur doit s'assurer que l'utilisateur final a bien donné au client l'autorisation d'accéder au serveur en son nom. Cela implique généralement de vérifier les portées (scopes) associées au jeton, afin de s'assurer qu'elles couvrent l'accès à la base de données selon les paramètres HBA actuels.

L'objectif de cette étape est d'éviter qu'un client OAuth n'obtienne un jeton sous de faux prétextes. Si le validateur exige que tous les jetons comportent des portées couvrant l'accès à la base de données, le fournisseur doit alors inciter explicitement l'utilisateur à accorder cet accès lors du processus. Cela lui donne l'opportunité de refuser la demande si le client n'est pas censé utiliser ses identifiants pour se connecter à des bases de données.

Bien qu'il soit possible d'établir l'autorisation du client sans portées explicites, en s'appuyant sur une connaissance externe de l'architecture déployée, cela écarte l'utilisateur du processus, l'empêchant ainsi de détecter d'éventuelles erreurs de déploiement, erreurs qui pourraient alors être exploitées silencieusement. L'accès à la base de données doit être strictement limité aux seuls clients de confiance [17] si aucune invite ne demande à l'utilisateur des portées supplémentaires.

Même si l'autorisation échoue, un module peut choisir de continuer à extraire les informations d'authentification du jeton à des fins d'audit et de débogage.

Authentifier l'utilisateur final

Enfin, le validateur doit déterminer un identifiant d'utilisateur associé au jeton, soit en demandant cette information au fournisseur, soit en l'extrayant directement du jeton, puis renvoyer cet identifiant au serveur (qui prendra ensuite une décision finale d'autorisation selon la configuration HBA). Cet identifiant sera disponible durant la session via system_user et enregistré dans les journaux du serveur si log_connections est activé.

Les différents fournisseurs peuvent enregistrer divers types d'informations d'authentification pour un utilisateur final, généralement appelés claims (revendications). En général, les fournisseurs documentent quelles revendications sont suffisamment fiables pour être utilisées dans les décisions d'autorisation, et lesquelles ne le sont pas (par exemple, il ne serait probablement pas judicieux d'utiliser le nom complet d'un utilisateur comme identifiant d'authentification, car beaucoup de fournisseurs permettent aux utilisateurs de modifier leur nom d'affichage de manière arbitraire). En définitive, le choix de la ou des revendications à utiliser dépend de l'implémentation du fournisseur et des besoins de l'application.

Note que la connexion anonyme ou pseudonyme est également possible, en activant la délégation via une table de correspondance des utilisateurs (usermap delegation) ; voir Section 50.1.3

50.1.2. Recommandations générales de développement #

Les développeurs doivent garder à l'esprit les éléments suivants lors de l'implémentation de la validation de jetons :

Confidentialité des jetons

Les modules ne doivent jamais écrire les jetons, ni même des parties de jetons, dans les journaux du serveur. Cela reste vrai même si le module considère le jeton comme invalide ; un attaquant qui parvient à tromper un client pour qu'il communique avec le mauvais fournisseur ne doit pas pouvoir récupérer ce jeton (valide par ailleurs) depuis le disque.

Les implémentations qui envoient des jetons sur le réseau (par exemple, pour effectuer une validation en ligne auprès d'un fournisseur) doivent authentifier le pair distant et s'assurer qu'un transport fortement sécurisé est utilisé.

Journalisation

Les modules peuvent utiliser les mêmes mécanismes de journalisation que les extensions standards ; toutefois, les règles pour l'émission de messages vers le client sont légèrement différentes pendant la phase d'authentification de la connexion. En règle générale, les modules doivent consigner les problèmes de vérification au niveau COMMERROR et retourner normalement, au lieu d'utiliser ERROR ou FATAL pour dérouler la pile, afin d'éviter toute fuite d'informations vers les clients non authentifiés.

Interruptibilité

Les modules doivent rester interruptibles par des signaux, afin que le serveur puisse gérer correctement les délais d'authentification et les signaux d'arrêt émis par pg_ctl. Par exemple, les appels bloquants sur des sockets doivent généralement être remplacés par du code capable de gérer à la fois les évènements sur les sockets et les interruptions sans condition de course (voir WaitLatchOrSocket(), WaitEventSetWait(), etc.), et les boucles longues doivent périodiquement appeler CHECK_FOR_INTERRUPTS(). Ne pas suivre ces recommandations peut entraîner des sessions serveur non réactives.

Tests

La couverture de tests complète d'un système OAuth dépasse le cadre de cette documentation, mais à minima, les tests négatifs doivent être considérés comme obligatoires. Il est trivial de concevoir un module qui laisse entrer les utilisateurs autorisés ; tout l'enjeu est d'empêcher l'accès aux utilisateurs non autorisés.

Documentation

Les implémentations de validateurs doivent documenter le contenu et le format de l'identifiant authentifié qui est transmis au serveur pour chaque utilisateur, car les administrateurs de base de données (DBA) peuvent avoir besoin de cette information pour créer les correspondances dans pg_ident.conf (par exemple, s'agit-il d'une adresse e-mail ? d'un identifiant organisationnel ? d'un UUID ?). Elles doivent aussi indiquer s'il est sûr d'utiliser le module avec l'option delegate_ident_mapping=1, ainsi que les éventuelles configurations supplémentaires nécessaires.

50.1.3. Autorisation des utilisateurs (Délégation via usermap) #

Le résultat standard d'un module de validation est un identifiant utilisateur, que le serveur comparera ensuite aux correspondances définies dans pg_ident.conf pour déterminer si l'utilisateur final est autorisé à se connecter. Toutefois, OAuth est en lui-même un cadre d'autorisation, et les jetons peuvent contenir des informations relatives aux privilèges de l'utilisateur. Par exemple, un jeton peut être associé aux groupes organisationnels auxquels appartient un utilisateur, ou à la liste des rôles qu'il peut assumer, et reproduire ces informations dans des fichiers de correspondance locaux sur chaque serveur n'est pas forcément souhaitable.

Pour contourner entièrement la correspondance des noms d'utilisateur et confier au module de validation la responsabilité supplémentaire d'autoriser les connexions utilisateur, le HBA peut être configuré avec delegate_ident_mapping. Le module peut alors utiliser les portées (scopes) du jeton ou une méthode équivalente pour décider si l'utilisateur est autorisé à se connecter avec le rôle souhaité. L'identifiant utilisateur sera toujours enregistré par le serveur, mais il n'interviendra plus dans la décision de poursuite de la connexion.

Avec ce mécanisme, l'authentification devient optionnelle, tant que le module signale que la connexion est autorisée, l'ouverture de session se poursuit, même si aucun identifiant utilisateur n'est renseigné. Cela permet de mettre en œuvre un accès anonyme ou pseudonyme à la base de données, où le fournisseur tiers effectue toute l'authentification nécessaire, sans fournir aucune information permettant d'identifier l'utilisateur au serveur (certains fournisseurs peuvent générer un identifiant anonymisé à enregistrer à des fins d'audit ultérieur).

La délégation via usermap offre une flexibilité architecturale maximale, mais elle fait du module de validation un point de défaillance unique pour l'autorisation des connexions. À utiliser avec prudence.



[17] C'est-à-dire, « de confiance » dans le sens où le client OAuth et le serveur PostgreSQL sont contrôlés par la même entité. Notamment, le flux de client d'autorisation par appareil (Device Authorization) pris en charge par libpq ne répond généralement pas à ce critère, car il est conçu pour des clients publics ou non fiables.