
공유 페이지 트리에서는 깔끔해 보였지만, 검색 엔드포인트는 다른 이야기를 전했습니다. Docmost에서, 공유 보기에서 숨겨진 제한된 하위 페이지가 공유 검색 결과를 통해 여전히 유출될 수 있습니다.
페이지 트리에서는 공유 항목이 깨끗해 보였지만, 검색 엔드포인트는 다른 이야기를 전했습니다. Docmost에서, 공개 공유 보기에서 숨겨진 제한된 하위 페이지가 여전히 공개 공유 검색 결과를 통해 유출될 수 있었습니다.
Docmost(오픈소스 협업 위키 및 문서화 플랫폼)를 검토하던 중 아주 간단한 질문에서 이 문제를 발견했습니다.
의도적으로 공개 공유 보기에서 숨겨진 페이지가 있다면, 모든 공개 기능이 동일한 제한 경계를 존중하는가?
이 경우, 답은 '아니오'였습니다.
제한된 하위 페이지는 공개 공유 트리에서는 숨겨져 있었지만, 공개 공유 검색 엔드포인트를 통해 여전히 유출되었습니다.
이 문제는 접수되어 CVE-2026-33146이 할당되었습니다.
Docmost: GitHub의 Docmost
CVE: CVE-2026-33146
Docmost의 공식 사이트는 이를 3M+ 다운로드를 기록한 엔터프라이즈 지원 온프레미스 위키로 소개하며, 빌니우스 시, Bechtle, 호주 정부, 적십자사, ETS Quebec을 포함한 조직의 팀이 신뢰한다고 밝히고 있습니다.
하위 페이지가 활성화된 공개 부모 공유 → 공개 트리에서 생략된 제한된 하위 → 공격자가 공개 공유 검색 쿼리 → 제한된 하위 제목 및 조각 유출
Docmost는 협업 위키 및 문서화 플랫폼입니다.
다음을 제공합니다:
즉, 공개 공유 모델은 실제 보안 경계입니다.
여기서 중요한 질문은 Docmost가 페이지를 공개적으로 공유할 수 있는지 여부가 아니었습니다.
진짜 질문은 다음과 같습니다.
Docmost가 하위 페이지를 제한되어 공개 공유 방문자에게 표시되어서는 안 된다고 결정했을 때, 그 제한이 공개 공유 흐름의 모든 곳에서 유지되는가?
이 경우, 그렇지 않았습니다.
많은 보안 검토는 UI에서 페이지가 숨겨진 것을 확인하면 너무 일찍 멈춥니다.
그것만으로는 충분하지 않습니다.
더 강력한 질문은 이것입니다.
모든 백엔드 경로가 동일한 가시성 결정을 적용하는가?
보안 경계는 인터페이스가 어떻게 보이는지가 아니라 서버가 실제로 무엇을 반환하는지에 의해 정의되기 때문에 이것이 중요합니다.
여기서 공개 트리 엔드포인트는 안전하게 작동했습니다:
그러나 공개 공유 검색 경로는 다르게 작동했습니다:
이로 인해 단순한 표시 불일치가 아니라 실제 인가 및 정보 공개 문제가 되었습니다.
무작위로 경로를 퍼징하고 흥미로운 것이 나타나길 바라는 방식으로 Docmost에 접근하지 않았습니다.
더 강력한 방법은 먼저 신뢰 경계를 선택하는 것이었습니다.
다음을 지원하는 애플리케이션의 경우:
가장 좋은 질문 중 하나는 다음과 같습니다.
검색 계층이 탐색 계층과 정확히 동일한 인가 경계를 적용하는가?
이 질문은 특히 다음과 같은 경우에 가치가 있습니다:
이 문제가 바로 그 지점에서 나타났습니다.
버그는 Docmost가 일반 공개 트리에서 제한된 페이지를 숨기지 못한 것이 아니었습니다.
버그는 공개 검색이 동일한 제한 로직을 준수하지 않은 것이었습니다.
소스 검토 결과, 공개 트리 흐름은 제한 인식 하위 항목 탐색을 사용했습니다.
관련 영역:
apps/server/src/core/share/share.service.ts해당 경로는 다음을 사용하여 제한된 하위 항목을 의도적으로 제외했습니다.
getPageAndDescendantsExcludingRestricted(...)그러나 공개 공유 검색 흐름은 다른 경로를 따랐습니다.
관련 영역:
apps/server/src/core/search/search.controller.tsapps/server/src/core/search/search.service.ts여기서 코드는 다음을 사용하여 하위 항목을 수집했습니다.
getPageAndDescendants(...)이는 제한된 하위 항목이 검색 범위에 남아 있음을 의미했습니다.
공개 공유 컨텍스트에서 이는 검색 분기가 일반 인증된 사용자 권한 컨텍스트 없이 실행되기 때문에 매우 중요합니다. 따라서 일단 제한된 하위 항목이 검색 가능한 페이지 집합에 포함되면 해당 메타데이터가 응답을 통해 유출될 수 있습니다.
공격자에게 인증된 계정이 필요하지 않기 때문입니다.
필요한 것은 다음과 같습니다:
이 조건이 충족되면 공개 방문자는 공유 검색 엔드포인트를 쿼리하여 다음을 복구할 수 있습니다:
전체 페이지 본문이 반환되지 않더라도 기밀성 유출을 생성하기에 충분합니다.
중요한 차이점은 애플리케이션이 이미 의도된 보안 모델을 명확하게 신호하고 있다는 것입니다.
공개 트리 엔드포인트는 제한된 하위 항목을 숨깁니다.
따라서 진짜 질문은 다음과 같습니다.
“검색이 우연히 더 넓은 결과 집합을 반환하는가?”
진짜 질문은 다음과 같습니다.
“검색이 동일한 공개 공유 경계에 대해 다른 곳에서 이미 적용된 인가 결정을 위반하는가?”
Docmost에서 그랬습니다.
이로 인해 다음에서 전환됩니다:
에서:
이것이 실제 취약점인 이유입니다.
두 관련 공개 엔드포인트를 나란히 비교하여 문제를 확인했습니다.
먼저 공개 공유 키를 사용하여 일반 공개 트리 엔드포인트를 테스트했습니다.
예시 요청:
POST /api/shares/tree HTTP/1.1
Host: 127.0.0.1:6752
Content-Type: application/json
{
"shareId": "public-share-key"
}
응답은 페이지 트리에 공개 하위 페이지만 반환했습니다.
대표 결과:
{
"pageTree": [
{
"id": "public-child",
"title": "Public roadmap"
}
]
}
이로써 예상된 제품 동작이 확립되었습니다:
그런 다음 제한된 하위 항목 내에 나타나는 용어를 사용하여 공개 공유 검색 엔드포인트를 쿼리했습니다.
예시 요청:
POST /api/search/share-search HTTP/1.1
Host: 127.0.0.1:6752
Content-Type: application/json
{
"shareId": "public-share-key",
"query": "salary"
}
응답은 여전히 제한된 하위 항목을 포함했습니다:
{
"items": [
{
"id": "public-child",
"title": "Public roadmap",
"highlight": "release plan and milestones"
},
{
"id": "restricted-child",
"title": "Payroll Q4",
"highlight": "salary bands and bonus targets"
}
]
}
이는 핵심 주장을 입증했습니다:
이 문제의 가장 강력한 부분은 두 번째 요청 자체가 아닙니다.
두 엔드포인트 간의 대비입니다.
이는 제품이 이미 공개 공유에 대한 의도된 제한 모델을 가지고 있음을 보여줍니다.
제한된 하위 항목은 공개 방문자에게 표시되어서는 안 됩니다.
이는 검색 경로가 정확히 동일한 경계를 위반함을 증명합니다.
이로 인해 이 문제를 예상된 검색 동작이나 문서화 격차로 무시하기가 더 어려워집니다.
애플리케이션 자체가 트리 응답을 통해 규칙을 설정한 다음 검색 응답을 통해 이를 위반합니다.
이는 강력한 증거입니다.
이 문제는 전체 작업 공간에 걸쳐 임의 콘텐츠를 노출하지 않습니다.
범위는 그보다 좁습니다.
그러나 영향을 받는 공개 공유 하위 트리 내에서 여전히 공격자에게 유용한 무단 지식을 제공합니다:
짧은 조각이라도 중요할 수 있습니다.
다음과 같은 제목:
이미 공격자에게 보안 가치를 창출합니다.
따라서 최종적으로 **보통(Moderate)**으로 분류되었지만, 깨끗하고 방어 가능한 경계 위반이 있는 유효한 기밀성 문제입니다.
이 문제에는 다음이 할당되었습니다:
권고 심각도는:
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:N/A:N이 점수는 완전한 무단 문서 접근보다는 더 좁은 기밀성 유출을 반영합니다.
중요한 점은 문제가 여전히 유효하다는 것입니다.
여기서 주장하는 것은 다음과 같습니다:
주장은 다음과 같습니다:
이는 실제 인가 관련 정보 공개입니다.
일부 사람들은 메타데이터 유출을 너무 빨리 무시합니다.
그것은 실수입니다.
진짜 질문은 유출된 데이터가 의도된 경계를 넘는지 여부입니다.
여기서는 그랬습니다.
애플리케이션이 다음과 같이 말한다면:
그러나 공개 엔드포인트가 여전히 다음을 드러낸다면:
그렇다면 영향이 제한적이더라도 기밀성 모델이 실패한 것입니다.
따라서 보고할 가치가 있습니다.
이와 같은 깨끗하고, 범위가 명확하며, 재현 가능한 버그는 강력한 보안 검토 판단을 입증하는 데 도움이 되는 정확한 종류의 문제입니다.
가장 안전한 수정 방향은 공개 검색이 공개 트리 흐름과 동일한 제한 인식 하위 항목 로직을 사용하도록 하는 것입니다.
실제로는 공유 검색 분기가 다음을 사용하여 하위 항목을 열거하지 않아야 함을 의미합니다.
getPageAndDescendants(...)
대신 더 안전한 공개 공유 탐색에 맞춰 다음을 사용해야 합니다.
getPageAndDescendantsExcludingRestricted(...)
또 다른 수정 방법은 더 넓은 열거를 유지한 다음 검색 쿼리가 결과를 반환하기 전에 제한된 하위 항목을 명시적으로 필터링하는 것입니다.
그러나 더 깔끔한 설계는 간단합니다.
검색 경계는 탐색 경계와 일치해야 합니다.
이것이 실패한 보안 속성입니다.
이 문제는 GitHub의 보안 보고 흐름을 통해 비공개로 보고되었습니다.
보고서는 다음을 보여주었습니다:
/api/shares/tree를 통한 의도된 안전한 동작/api/search/share-search를 통한 일관되지 않은 취약한 동작문제가 접수되어 다음이 할당되었습니다:
CVE-2026-33146
최종 권고 심각도는 **보통(Moderate)**으로, 더 넓은 심각성 주장보다 좁은 유출 범위에 더 잘 맞습니다.
이는 발견의 유효성을 약화시키지 않습니다.
단지 그 영향을 더 정확하게 정의합니다.
핵심 교훈은 간단합니다.
하나의 공개 엔드포인트에서 무언가를 숨기는 것만으로는 충분하지 않습니다. 다른 공개 엔드포인트가 여전히 그것을 드러낸다면.
많은 개발자는 인가를 명백한 렌더링 경로에서만 생각합니다:
그러나 실제 경계는 더 넓습니다.
다음과 같은 질문도 해야 합니다:
Docmost에서 답은 '아니오'였습니다.
그것이 진짜 핵심입니다.
이 취약점은 화려한 페이로드나 복잡한 익스플로잇 체인에 관한 것이 아니었습니다.
매우 실용적인 신뢰 경계 질문을 하는 것이었습니다.
Docmost는 한 곳에서 제한된 페이지를 숨겼습니다. 그런 다음 다른 곳에서 유출했습니다.
그것이 CVE-2026-33146이 된 이유입니다.