
Ссылки на общие базы NocoDB могут приглашать реальных участников базы и сохраняться после отзыва общего доступа
Ссылки совместного доступа к базам NocoDB могут приглашать реальных участников базы и переживать отзыв доступа
Я нашел эту проблему при аудите NocoDB, задав простой вопрос безопасности:
Может ли публичная ссылка на общую базу пересечь границу от временного общего доступа к реальной аутентифицированной роли участника базы?
В данном случае ответ был «да».
Сессия, аутентифицированная только по xc-shared-base-id, рассматривалась как обычный читатель базы для целей контроля доступа (ACL). Поскольку права читателя всё ещё включали конечные точки управления участниками, пользователь, имеющий только UUID общей базы, мог перечислить существующих участников базы и пригласить произвольный email-адрес в базу как реального участника.
Приглашённый пользователь затем мог активировать приглашение через обычный процесс регистрации, получить стандартную аутентифицированную учётную запись и сохранить доступ к базе даже после того, как владелец отключил общую ссылку.
Эта проблема получила идентификатор CVE-2026-46552.
Проект: NocoDB
Подтверждённая уязвимая версия: 0.301.3
Это затронуло NocoDB. На официальном сайте NocoDB представлен как платформа, которой доверяют более 35 000 организаций, с более 20 миллионами загрузок. На сайте также перечислены такие компании, как Accenture, Western Digital, Hyundai, Walmart, PwC, Bosch и American Express.
публичная ссылка на общую базу -> xc-shared-base-id воспринимается как обычный читатель базы -> ACL читателя открывает конечные точки управления участниками -> злоумышленник перечисляет пользователей базы и приглашает произвольный email -> приглашённый пользователь активирует обычный токен регистрации -> долговременный аутентифицированный доступ к базе переживает отзыв общей ссылки
NocoDB — это платформа для совместной работы, ориентированная на базы данных, которая предоставляет браузерный доступ к базам, возможность публикации, управления метаданными и рабочими процессами управления участниками.
Это означает, что модель публикации является реальной границей безопасности.
Важный вопрос здесь заключался не в том, могут ли ссылки на общие базы читать опубликованный контент.
Настоящий вопрос был:
Может ли субъект публичного доступа выполнять действия, которые должны принадлежать только аутентифицированным участникам базы?
В данном случае — мог.
Функции публичного доступа легко недооценить.
Это ошибка.
Как только приложение поддерживает:
основной риск заключается не только в раскрытии данных.
Более сильный риск — это коллапс границ:
В этом и заключалась реальная проблема.
Это не была ошибка проверки входа. Это не была проблема подделки токенов. Это не был дефект сброса пароля.
Это был классический отказ границы авторизации:
Я подошёл к этому не путём случайного зондирования конечных точек в надежде, что что-то интересное ответит.
Более эффективным путём было сначала определить границу доверия с наибольшей ценностью.
Для NocoDB это была граница между:
Эти два состояния не должны быть взаимозаменяемыми.
Ссылка на общую базу должна предоставлять ограниченный, отзываемый доступ на основе ссылки. Она не должна иметь возможности создавать новые долговременные субъекты внутри базы.
Именно эта граница и была нарушена.
Уязвимость возникла из-за того, как доступ к общей базе был интегрирован в обычный путь ACL.
В клиентской части для общей базы вставлялся xc-shared-base-id, при этом обычные заголовки аутентификации удалялись.
Затем на серверной стороне BaseViewStrategy принимал xc-shared-base-id и напрямую преобразовывал общую ссылку в обычные roles / base_roles, полученные из конфигурации общей базы.
Это была первая проблема.
Вторая проблема заключалась в том, что права уровня читателя всё ещё включали действия по управлению участниками.
В слое ACL, ProjectRoles.VIEWER имел доступ к:
baseUserListuserInviteЭти права защищали обычные маршруты метаданных:
GET /api/v2/meta/bases/:baseId/usersPOST /api/v2/meta/bases/:baseId/usersТаким образом, сессия публичного доступа фактически могла обращаться к конечным точкам членства, предназначенным для реальных участников базы.
Последний шаг был в самом потоке приглашений.
BaseUsersService.userInvite() проверял силу роли, а затем создавал:
invite_tokenА для сессий общей базы:
invited_by становился nullпотому что за запросом не было реальной аутентифицированной личности приглашающего.
Это и есть вся цепочка ошибок.
Потому что обладания ссылкой на общую базу было достаточно.
Злоумышленнику не требовалось:
xc-authЦепочка эксплуатации была прямолинейной:
Это преобразует отзываемый общий доступ в постоянное членство.
Важное различие — сохранение доступа после отзыва.
Это не было просто:
«читатель мог вызвать конечную точку читателя»
Уязвимым субъектом был не обычный аутентифицированный читатель.
Это была сессия публичного доступа.
Это важно, потому что приложение считало временный субъект, ограниченный ссылкой, достаточно доверенным для того, чтобы:
Настоящий вопрос был не в том:
«Может ли общий пользователь читать общие данные?»
Настоящий вопрос был в том:
«Может ли публичный доступ быть преобразован в постоянный аутентифицированный доступ, который переживёт отзыв общего доступа?»
Ответ был «да».
Вот почему это реальная уязвимость авторизации, а не просто удивительное поведение приложения.
Я проверил это локально на версии:
0.301.3dac49b0122c5ee655fb8f46a1b6e42dfeec1f3adhttp://127.0.0.1:8080Воспроизведение было простым.
Сначала я вошёл как обычная учётная запись владельца, создал новую базу, создал таблицу и включил общий доступ к базе с ролью viewer:
PATCH /api/v2/meta/bases/<baseId>/shared
Content-Type: application/json
{
"roles": "viewer"
}
Это вернуло UUID общей базы.
Затем, не отправляя xc-auth, я использовал только:
xc-shared-base-id: <sharedBaseUuid>
Используя только этот заголовок, я вызвал:
GET /api/v2/meta/bases/<baseId>/users
Это вернуло 200 OK и раскрыло реальных участников базы, включая адреса электронной почты.
Всё ещё используя только xc-shared-base-id, я вызвал:
POST /api/v2/meta/bases/<baseId>/users
Content-Type: application/json
{
"email": "[email protected]",
"roles": "viewer"
}
Это также вернуло 200 OK.
Для локальной проверки в лаборатории без доставки почты я подтвердил напрямую в мета-базе SQLite, что:
nc_users_v2 содержал приглашённого пользователя с непустым invite_tokennc_base_users_v2 содержал реальную строку членства для целевой базыinvited_by был NULLЗатем я активировал приглашение через обычный процесс регистрации:
POST /api/v2/auth/user/signup
Content-Type: application/json
{
"email": "[email protected]",
"password": "Password123.",
"token": "<invite_token>"
}
Используя возвращённый xc-auth, я вызвал:
GET /api/v2/meta/bases/<baseId>/tables
Это вернуло 200 OK.
Наконец, как владелец, я отключил общую ссылку:
DELETE /api/v2/meta/bases/<baseId>/shared
После этого:
xc-shared-base-id завершился с ошибкой 401xc-auth, всё ещё успешно получала 200200200200200200401200Это подтвердило центральное утверждение безопасности:
Одного успешного приглашения через общую базу уже было бы достаточно, чтобы показать нарушение авторизации.
Но полная цепочка проверки была важна по двум причинам.
Она показала, что это не просто раскрытие конечной точки.
Сессия публичного доступа не просто обращалась к ограниченному API. Она завершила полную цепочку повышения привилегий:
Она доказала, что это не является самоотзываемым.
Более серьёзное воздействие проявилось после отключения общей ссылки:
Именно это превратило временный доступ по ссылке в долговременное сохранение доступа.
Эта уязвимость позволяет любому, у кого есть ссылка на общую базу:
Основное воздействие — нарушение конфиденциальности, потому что злоумышленник может сохранять долговременный доступ на чтение к данным общей базы через обычную аутентифицированную учётную запись.
Также есть воздействие на целостность, потому что субъект публичного доступа может изменять состояние контроля доступа, добавляя новых участников в базу.
Это более серьёзный результат, чем обычная утечка данных. Это нарушение границы привилегий между анонимным доступом и аутентифицированным членством.
Эта проблема обоснованно классифицируется как ошибка авторизации с пересечением областей и воздействием на конфиденциальность.
CVSS:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N
Этот вектор соответствует основному поведению:
Направление исправления очевидно.
Сессии общих баз не должны наследовать возможности управления участниками.
Как минимум:
baseUserList и userInvite из любых прав, доступных через xc-shared-base-idGET и POST /api/v2/meta/bases/:baseId/usersЭта проблема была проверена локально на NocoDB 0.301.3 на коммите dac49b0122c5ee655fb8f46a1b6e42dfeec1f3ad.
Отчёт демонстрировал:
xc-shared-base-id в обычные роли базыПроблеме был присвоен идентификатор:
CVE-2026-46552
Ключевой урок прост:
доступ по общей ссылке — это не то же самое, что доверенное членство.
Многие системы попадают в неприятности, когда объединяют эти две концепции в одной ролевой модели.
Общая ссылка может выглядеть операционно похожей на учётную запись читателя, но предположения о доверии разные:
Если этот субъект с низким уровнем доверия может выполнять действия по управлению или создавать новые долговременные идентификаторы, граница общего доступа уже нарушена.
Вот настоящий вывод.
Эта уязвимость заключалась не в полном обходе аутентификации.
Она заключалась в объединении двух уровней доверия, которые должны были оставаться разделёнными.
В NocoDB ссылка на общую базу должна была предоставлять временный, отзываемый доступ к опубликованному контенту. Вместо этого её можно было использовать для перечисления участников, приглашения реального пользователя в базу и преобразования публичного доступа в долговременное аутентифицированное членство, которое переживало отзыв общей ссылки.
Вот почему это стало CVE-2026-46552.