ShareDB names presence channels '$presence.' + channel, in the same pub/sub namespace as its op channels (collection and collection + '.' + id). A collection named $presence, or starting with $presence., publishes its ops on channels that presence subscribers listen to:
$presence.secrets goes to the collection channel $presence.secrets, which is the presence channel for connection.getPresence('secrets');$presence.secrets/alice publishes to $presence.secrets.alice, the presence channel for connection.getPresence('secrets.alice');$presence/notes.alice publishes to $presence.notes.alice, the presence channel for doc presence on notes/alice.
The agent sends everything that arrives on a presence stream to the client once sendPresence middleware passes it, without checking that it is presence: Agent._handlePresenceData only looks at src and r. So these ops reach a presence subscriber raw, and readSnapshots / op middleware and projections never run on them. Untyped presence channels are arbitrary client-chosen strings, so any client can subscribe to the matching one.Information disclosure / authorization bypass, for deployments with a collection named $presence or starting with $presence.. Any client allowed to subscribe to presence can read that collection's ops, including create data, which readSnapshots / op middleware would deny it. The official client logs the message as unrecognized and drops it, but by then it has been sent over the connection.
The precondition is rare. Such collection names are unusual, and MongoDB doesn't allow $ in collection names, so sharedb-mongo deployments aren't affected; backends that accept any collection name, such as the in-memory DB or sharedb-postgres, can be. A sendPresence middleware that authorizes on context.collection sees the op's own collection, and blocks it if it denies that collection. Hence low.
With middleware that denies collections starting with $presence to non-admins, and no sendPresence middleware:
function denySecrets(context, next) {
var collection = context.collection;
if (!context.agent.custom.admin && typeof collection === 'string' && collection.indexOf('$presence') === 0) {
return next(new Error('Forbidden'));
}
next();
}
backend.use('readSnapshots', denySecrets);
backend.use('op', denySecrets);a non-admin client:
fetch() of $presence.secrets/alice with Forbidden, butconnection.getPresence('secrets') without error, and$presence.secrets/alice with {pin: '1234'}, receives:{"src": "…", "seq": 1, "v": 0, "create": {"type": "http://sharejs.org/types/JSONv0", "data": {"pin": "1234"}}, "c": "$presence.secrets", "d": "alice"}Subscribing to getPresence('secrets.alice'), or to doc presence on notes/alice while an admin creates $presence/notes.alice, gets the same.
Fixed in 7.0.2. The agent now drops anything on a presence stream that isn't presence or a presence request for the subscribed channel, before sendPresence middleware runs. The channel names stay as they are, so servers on different versions that share a pub/sub adapter keep exchanging presence during a rolling upgrade.
The check runs on the server the subscriber is connected to, so every server sharing the pub/sub needs the upgrade. Use the workaround below on any that can't be upgraded yet.
Reject anything that isn't presence in sendPresence middleware:
backend.use('sendPresence', function(context, next) {
if (context.presence.a !== 'p') return next(new Error('Not presence'));
next();
});Presence requests are answered before this middleware runs, so they're unaffected. Tested with doNotForwardSendPresenceErrorsToClient: true, so each rejection is logged rather than sent to the client. Alternatively, don't use collection names that start with $presence.
= 1.3.0, <= 7.0.1. Presence, and its channel naming, arrived in 1.3.0. Fixed in 7.0.2.
소스: NVD
무료 취약점 평가
9개 보안 영역에서 클라우드 보안 관행을 평가하여 위험 수준을 벤치마킹하고 방어의 허점을 파악하세요.
맞춤형 데모를 받아보세요
"내가 본 최고의 사용자 경험은 클라우드 워크로드에 대한 완전한 가시성을 제공합니다."
"Wiz는 클라우드 환경에서 무슨 일이 일어나고 있는지 볼 수 있는 단일 창을 제공합니다."
"우리는 Wiz가 무언가를 중요한 것으로 식별하면 실제로 중요하다는 것을 알고 있습니다."