
Les liens de base partagée de NocoDB pourraient inviter de vrais membres de la base et survivre à la révocation du partage
Les liens partagés de base NocoDB peuvent inviter des membres réels et survivre à la révocation du partage
J’ai découvert ce problème en examinant NocoDB, avec une simple question de sécurité en tête :
Un lien de base partagé public peut-il franchir la frontière entre un accès partagé temporaire et une véritable adhésion authentifiée à la base ?
Dans ce cas, la réponse était oui.
Une session de base partagée authentifiée uniquement par xc-shared-base-id était traitée comme un simple visualiseur de base pour les besoins de la liste de contrôle d’accès (ACL). Comme les permissions de visualiseur atteignaient encore les points de terminaison de gestion des membres, un utilisateur disposant uniquement de l’UUID de la base partagée pouvait énumérer les membres existants de la base et inviter une adresse e-mail arbitraire dans la base en tant que membre réel.
Cet utilisateur invité pouvait alors utiliser le jeton d’invitation via le flux d’inscription normal, obtenir un compte authentifié standard et conserver l’accès à la base même après que le propriétaire a désactivé le lien de base partagé.
Ce problème est devenu CVE-2026-46552.
Projet : NocoDB
Version affectée validée : 0.301.3
Cela affectait NocoDB. Sur son site officiel, NocoDB se présente comme étant utilisé par 35 000+ organisations, avec 20 millions+ de téléchargements. Le site mentionne également des entreprises comme Accenture, Western Digital, Hyundai, Walmart, PwC, Bosch et American Express.
lien de base partagée public -> xc-shared-base-id traité comme un simple visualiseur de base -> l’ACL du visualiseur atteint les points de terminaison de gestion des membres -> l’attaquant liste les utilisateurs de la base et invite une adresse e-mail arbitraire -> l’utilisateur invité utilise le jeton d’inscription normal -> l’accès authentifié durable à la base survit à la révocation du lien partagé
NocoDB est une plateforme collaborative orientée base de données qui expose l’accès à la base via navigateur, le partage, la gestion des métadonnées et les workflows d’adhésion.
Cela signifie que son modèle de partage constitue une véritable frontière de sécurité.
La question importante ici n’était pas de savoir si les liens de base partagée peuvent lire le contenu partagé.
La vraie question était :
Un principal de partage public peut-il effectuer des actions qui devraient appartenir uniquement aux membres authentifiés de la base ?
Dans ce cas, oui.
Les fonctionnalités de partage public sont faciles à sous-estimer.
C’est une erreur.
Dès qu’une application prend en charge :
le risque principal n’est pas seulement l’exposition des données.
Le risque plus fort est l’effondrement des frontières :
C’était le vrai problème ici.
Ce n’était pas un bogue dans la validation de connexion. Ce n’était pas un problème de falsification de jeton. Ce n’était pas un défaut de réinitialisation de mot de passe.
C’était un échec classique de frontière d’autorisation :
Je n’ai pas abordé cela en sondant au hasard des points de terminaison en espérant qu’un réponde intéressant.
La voie la plus solide était d’identifier d’abord la frontière de confiance à plus haute valeur.
Pour NocoDB, c’était la frontière entre :
Ces deux états ne devraient pas être interchangeables.
Un lien de base partagée est censé représenter un accès limité, révocable, basé sur un lien. Il ne devrait pas pouvoir créer de nouveaux principaux à longue durée de vie à l’intérieur de la base.
C’est exactement la frontière qui a échoué ici.
La vulnérabilité provenait de la manière dont l’accès à la base partagée était intégré dans le chemin normal de l’ACL.
Dans le flux frontal de la base partagée, xc-shared-base-id était injecté tandis que les en-têtes d’authentification normaux étaient supprimés.
Ensuite, dans le backend, BaseViewStrategy acceptait xc-shared-base-id et traduisait directement le lien partagé en roles / base_roles normaux dérivés de la configuration de la base partagée.
C’était le premier problème.
Le second problème était que les permissions de niveau visualiseur incluaient encore des actions de gestion des membres.
Dans la couche ACL, ProjectRoles.VIEWER pouvait atteindre :
baseUserListuserInviteCes permissions protégeaient les routes meta normales :
GET /api/v2/meta/bases/:baseId/usersPOST /api/v2/meta/bases/:baseId/usersAinsi, une session de partage public était effectivement autorisée à frapper des points de terminaison d’adhésion destinés aux participants réels de la base.
La dernière étape se trouvait dans le flux d’invitation lui-même.
BaseUsersService.userInvite() vérifiait la puissance du rôle, puis créait :
invite_tokenEt pour les sessions de base partagée :
invited_by devenait nullcar il n’y avait pas de véritable identité d’inviteur authentifié derrière la requête.
Voilà toute la chaîne du bogue.
Parce que la possession d’un lien de base partagée suffisait.
L’attaquant n’avait pas besoin de :
xc-authLa chaîne d’exploitation était simple :
Cela convertit le partage de lien révocable en adhésion durable.
La distinction importante est la persistance après révocation.
Ce n’était pas simplement :
« un visualiseur pouvait appeler un point de terminaison de visualiseur »
Le principal vulnérable n’était pas un visualiseur authentifié normal.
C’était une session de partage public.
Cela compte parce que l’application traitait un principal transitoire, limité à un lien, comme suffisamment fiable pour :
La vraie question n’était pas :
« Un utilisateur partagé peut-il lire les données partagées ? »
La vraie question était :
« Un accès de partage public peut-il être converti en un accès authentifié permanent qui survit à la révocation du partage ? »
La réponse était oui.
C’est pourquoi il s’agit d’une véritable vulnérabilité d’autorisation, et non d’un simple comportement surprenant de l’application.
J’ai validé cela localement avec :
0.301.3dac49b0122c5ee655fb8f46a1b6e42dfeec1f3adhttp://127.0.0.1:8080La reproduction était simple.
D’abord, je me suis connecté en tant que propriétaire normal, j’ai créé une nouvelle base, créé une table, et activé l’accès partagé en tant que viewer :
PATCH /api/v2/meta/bases/<baseId>/shared
Content-Type: application/json
{
"roles": "viewer"
}
Cela a renvoyé l’UUID de la base partagée.
Ensuite, sans envoyer de xc-auth, j’ai utilisé uniquement :
xc-shared-base-id: <sharedBaseUuid>
Avec seulement cet en-tête, j’ai appelé :
GET /api/v2/meta/bases/<baseId>/users
Cela a renvoyé 200 OK et exposé les vrais membres de la base, y compris les adresses e-mail.
Toujours en utilisant uniquement xc-shared-base-id, j’ai appelé :
POST /api/v2/meta/bases/<baseId>/users
Content-Type: application/json
{
"email": "[email protected]",
"roles": "viewer"
}
Cela a également renvoyé 200 OK.
Pour la validation locale en laboratoire sans livraison d’e-mail, j’ai confirmé directement dans la base de données meta SQLite que :
nc_users_v2 contenait l’utilisateur invité avec un invite_token non nulnc_base_users_v2 contenait une vraie ligne d’adhésion pour la base cibleinvited_by était NULLPuis j’ai utilisé le jeton d’invitation via le flux d’inscription normal :
POST /api/v2/auth/user/signup
Content-Type: application/json
{
"email": "[email protected]",
"password": "Password123.",
"token": "<invite_token>"
}
En utilisant le xc-auth renvoyé, j’ai appelé :
GET /api/v2/meta/bases/<baseId>/tables
Cela a renvoyé 200 OK.
Enfin, en tant que propriétaire, j’ai désactivé le lien de base partagée :
DELETE /api/v2/meta/bases/<baseId>/shared
Après cela :
xc-shared-base-id a échoué avec 401xc-auth normal a toujours réussi avec 200200200200200200401200Cela a établi la revendication centrale de sécurité :
Une seule invitation réussie via base partagée aurait déjà suffi à montrer un échec d’autorisation.
Mais la chaîne de validation complète importait pour deux raisons.
Cela montrait qu’il ne s’agissait pas seulement d’une exposition de point de terminaison.
La session de partage public n’a pas simplement atteint une API restreinte. Elle a accompli la chaîne complète de conversion de privilège :
Cela prouvait que ce n’était pas auto-révocable.
L’impact le plus sérieux est survenu après la désactivation du lien partagé :
C’est ce qui a transformé un accès temporaire par lien en une persistance d’accès durable.
Cette vulnérabilité permet à quiconque possède un lien de base partagée de :
L’impact principal est la confidentialité, car un attaquant peut maintenir un accès en lecture durable aux données de la base partagée via un compte authentifié ordinaire.
Il y a aussi un impact sur l’intégrité, car un principal de partage public peut modifier l’état de contrôle d’accès en ajoutant de nouveaux membres à la base.
C’est un résultat plus fort qu’une simple fuite de données. C’est une rupture de frontière de privilège entre le partage anonyme et l’adhésion authentifiée.
Ce problème est raisonnablement classé comme un défaut d’autorisation inter-contexte avec impact sur la confidentialité.
CVSS :
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N
Ce vecteur correspond au comportement central ici :
La direction de correction est simple.
Les sessions de base partagée ne devraient pas hériter des capacités de gestion des membres.
Au minimum :
baseUserList et userInvite de toutes les permissions accessibles via xc-shared-base-idGET et POST /api/v2/meta/bases/:baseId/usersCe problème a été validé localement contre NocoDB 0.301.3 sur le commit dac49b0122c5ee655fb8f46a1b6e42dfeec1f3ad.
Le rapport a démontré :
xc-shared-base-id dans les rôles normaux de baseLe problème a reçu l’identifiant :
CVE-2026-46552
La leçon clé ici est simple :
l’accès via lien partagé n’est pas la même chose qu’une adhésion de confiance.
Beaucoup de systèmes se retrouvent en difficulté lorsqu’ils fusionnent ces deux idées dans un même modèle de rôles.
Un lien partagé peut sembler opérationnellement similaire à un compte visualiseur, mais les hypothèses de confiance sont différentes :
Si ce principal à plus faible assurance peut effectuer des actions de gestion ou créer de nouvelles identités durables, la frontière du partage est déjà brisée.
C’est le véritable enseignement.
Cette vulnérabilité ne concernait pas le contournement complet de l’authentification.
Elle concernait la fusion de deux niveaux de confiance qui auraient dû rester séparés.
Dans NocoDB, un lien de base partagée était censé fournir un accès temporaire et révocable au contenu partagé. Au lieu de cela, il pouvait être utilisé pour énumérer les membres, inviter un véritable utilisateur dans la base et convertir l’accès public partagé en adhésion authentifiée durable qui survit à la révocation du partage.
C’est pourquoi cela est devenu CVE-2026-46552.