
NocoDB 공유 베이스 링크가 실제 베이스 구성원을 초대할 수 있으며 공유 취소 후에도 유지됩니다.
NocoDB 공유 베이스 링크가 실제 베이스 멤버를 초대할 수 있고 공유 취소 후에도 유지될 수 있음
NocoDB를 검토하던 중 이 문제를 발견했으며, 간단한 보안 질문을 염두에 두었습니다:
공유 베이스 링크가 일시적인 공유 접근에서 실제 인증된 베이스 멤버십으로 경계를 넘을 수 있는가?
이 경우, 답은 '예'였습니다.
xc-shared-base-id로만 인증된 공유 베이스 세션은 ACL 목적상 일반 베이스 뷰어로 취급되었습니다. 뷰어 권한이 여전히 멤버 관리 엔드포인트에 도달할 수 있었기 때문에, 공유 베이스 UUID만 가진 사용자가 기존 베이스 멤버를 열거하고 임의의 이메일 주소를 베이스에 실제 멤버로 초대할 수 있었습니다.
초대된 사용자는 일반 가입 흐름을 통해 초대를 수락하고, 표준 인증된 계정을 얻은 후, 소유자가 공유 베이스 링크를 비활성화해도 베이스 접근 권한을 유지할 수 있었습니다.
이 문제는 CVE-2026-46552가 되었습니다.
프로젝트: NocoDB
확인된 영향을 받는 버전: 0.301.3
이는 NocoDB에 영향을 미쳤으며, 공식 사이트에서 NocoDB는 35,000개 이상의 조직이 신뢰하고 2천만 회 이상 다운로드된 것으로 소개됩니다. 또한 사이트는 Accenture, Western Digital, Hyundai, Walmart, PwC, Bosch, American Express와 같은 기업을 나열합니다.
공개 공유 베이스 링크 -> xc-shared-base-id가 일반 베이스 뷰어로 처리됨 -> 뷰어 ACL이 멤버 관리 엔드포인트에 도달함 -> 공격자가 베이스 사용자를 나열하고 임의 이메일 초대 -> 초대된 사용자가 일반 가입 토큰 수락 -> 공유 링크 취소 후에도 지속되는 인증된 베이스 접근 유지
NocoDB는 데이터베이스 기반 협업 플랫폼으로, 브라우저 기반 베이스 접근, 공유, 메타데이터 관리 및 사용자 멤버십 워크플로우를 노출합니다.
즉, 공유 모델은 실제 보안 경계입니다.
여기서 중요한 질문은 공유 베이스 링크가 공유 콘텐츠를 읽을 수 있는지 여부가 아니었습니다.
진짜 질문은:
공개 공유 주체가 인증된 베이스 멤버에게만 속해야 하는 작업을 수행할 수 있는가?
이 경우, 가능했습니다.
공개 공유 기능은 과소평가되기 쉽습니다.
그것은 실수입니다.
애플리케이션이 다음을 지원하면:
주된 위험은 단순한 데이터 노출만이 아닙니다.
더 강력한 위험은 경계 붕괴(boundary collapse) 입니다:
그것이 여기서 실제 문제였습니다.
로그인 검증의 버그가 아니었습니다. 토큰 위조 문제도 아니었습니다. 비밀번호 재설정 결함도 아니었습니다.
이는 고전적인 권한 부여 경계 실패(authorization boundary failure) 였습니다:
무작위로 엔드포인트를 찔러보고 흥미로운 응답을 기대하는 방식으로 접근하지 않았습니다.
더 강력한 경로는 먼저 가장 가치 있는 신뢰 경계를 식별하는 것이었습니다.
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_token이 null이 아님nc_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를 사용한 공유 링크 접근은 401 실패xc-auth를 사용한 초대된 계정은 200으로 계속 성공200200200200200401200이는 중앙 보안 주장을 입증했습니다:
한 번의 성공적인 공유 베이스 초대로도 이미 권한 부여 실패를 보여주기에 충분했을 것입니다.
그러나 전체 검증 체인은 두 가지 이유로 중요했습니다.
이것이 단순한 엔드포인트 노출이 아님을 보여주었습니다.
공개 공유 세션이 단순히 제한된 API에 도달한 것이 아닙니다. 전체 권한 전환 체인을 완료했습니다:
이것이 자체 취소되지 않음을 증명했습니다.
더 심각한 영향은 공유 링크 비활성화 후에 나타났습니다:
이것이 일시적인 링크 접근을 지속적인 접근 유지로 바꾼 것입니다.
이 취약점은 공유 베이스 링크를 가진 모든 사람이 다음을 수행할 수 있게 합니다:
주된 영향은 기밀성(confidentiality) 입니다. 공격자가 일반 인증된 계정을 통해 공유 베이스 데이터에 대한 지속적인 읽기 접근을 유지할 수 있기 때문입니다.
또한 무결성(integrity) 영향도 있습니다. 공개 공유 주체가 새 멤버를 추가하여 접근 제어 상태를 수정할 수 있기 때문입니다.
이는 일반적인 데이터 유출보다 더 강력한 결과입니다. 익명 공유와 인증된 멤버십 사이의 권한 경계 붕괴입니다.
이 문제는 기밀성 영향을 수반하는 교차 범위 권한 부여 결함으로 합리적으로 분류됩니다.
CVSS:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N
해당 벡터는 여기서의 핵심 동작에 적합합니다:
수정 방향은 간단합니다.
공유 베이스 세션은 멤버 관리 기능을 상속해서는 안 됩니다.
최소한:
xc-shared-base-id를 통해 접근 가능한 모든 권한에서 baseUserList 및 userInvite 제거GET 및 POST /api/v2/meta/bases/:baseId/users와 같은 베이스 멤버십 엔드포인트를 호출할 수 없도록 명시적 차단 시행이 문제는 NocoDB 0.301.3의 커밋 dac49b0122c5ee655fb8f46a1b6e42dfeec1f3ad에서 로컬로 검증되었습니다.
보고서는 다음을 입증했습니다:
xc-shared-base-id에서 일반 베이스 역할로의 ACL 매핑해당 문제는 다음으로 할당되었습니다:
CVE-2026-46552
핵심 교훈은 간단합니다:
공유 링크 접근은 신뢰된 멤버십과 동일하지 않습니다.
많은 시스템이 이 두 개념을 동일한 역할 모델로 통합할 때 문제에 빠집니다.
공유 링크는 운영상 뷰어 계정과 유사해 보일 수 있지만, 신뢰 가정은 다릅니다:
이 낮은 보증의 주체가 관리 작업을 수행하거나 새로운 지속적인 신원을 생성할 수 있다면, 공유 경계는 이미 깨진 것입니다.
그것이 진짜 핵심입니다.
이 취약점은 인증을 완전히 우회하는 것에 관한 것이 아니었습니다.
분리되어야 했던 두 신뢰 수준을 붕괴시키는 것에 관한 것이었습니다.
NocoDB에서 공유 베이스 링크는 일시적이고 취소 가능한 공유 콘텐츠 접근을 제공하기로 되어 있었습니다. 대신, 멤버를 열거하고, 베이스에 실제 사용자를 초대하며, 공유 취소 후에도 지속되는 인증된 멤버십으로 공개 공유 접근을 전환하는 데 사용될 수 있었습니다.
그것이 이것을 CVE-2026-46552로 만든 이유입니다.