Articles / Privacy

  • signal
  • group-chat
  • metadata
  • traffic-analysis
  • privacy
  • encryption

Hidden Group Membership Still Leaks When the Server Delivers

You can encrypt a group's member list so the server cannot read it, and the server will still work out who is in the group. It does so by doing its job: delivering messages. This post is about why that gap exists and why it is hard to close.

The short version is that "the server stores no plaintext member list" and "the server does not know the members" are different claims. The first is a storage property. The second is an observation property, and delivery breaks it.

What the encrypted group state does protect

In a Signal-style design, the group's title, avatar and member list live on the server as ciphertext. The key is a group secret that only members hold. Members prove they belong using credentials built so the server can check them without learning which account presented them.

That is a real improvement over a server that keeps a plain table of group_id, user_id rows. A database dump, a subpoena for stored data or a curious administrator gets blobs rather than names.

  • The server holds encrypted group state.
  • Member identifiers inside that state are encrypted under the group key.
  • Membership proofs are zero-knowledge, so the check does not name the member.

All of that is about data at rest, and about requests that manage the group. Sending a message is a different path.

Delivery needs a list of recipients

When you send to a group, the server has to put a copy in each member's queue. To do that it must be told, one way or another, which accounts to deliver to. The recipient list for a send is visible to the server, even if the group's stored member list is not.

There are two ways a client can supply it:

  1. Send one request per member, each addressed to that member's account.
  2. Send one request carrying a set of per-recipient ciphertexts, addressed to a list of accounts.

Either way, the server sees a burst of deliveries to the same set of accounts at the same moment. The set is the membership, or the currently reachable part of it.

Sealed sender hides the other end, not this one

Sealed sender removes the sender's identity from the envelope the server sees. It does not remove the destination, because the server cannot route to nobody. So it helps with "who sent this" and does little for "who received it".

It is worth keeping the two questions apart. A design can hide the author of every message and still show a perfect recipient set, repeatedly, with timestamps.

Quick detour: why not just hide the recipients too?

Hang on, why can't the client simply say "deliver this to group X" and let the server fan it out? Because then the server needs a member list in the clear to fan out to. That is exactly the thing we encrypted.

The alternative is to let the client prove it holds the right to send to the group, and name the recipients anyway. Signal's more recent group send endorsements go this way, as I understand them: a credential lets a member send to the others without the server holding a plain list. The recipients are still named per send, though. The credential changes who can send, not what the server sees.

What an observer can reconstruct

Delivery logs are enough to cluster accounts. You do not need to break any cryptography, only count.

  • Accounts that always receive within milliseconds of each other likely share a group.
  • The same recipient set appearing again and again is a group, whatever its name.
  • Set size is roughly the group size.
  • Subsets that drop out or arrive late hint at who is offline or has muted the chat.

Add message sizes and timing and you also get activity patterns: when the group is busy, who replies fastest, which members never do. None of that touches message content.

This is the same family of leak as traffic analysis in general. Encryption protects the payload; the shape of the traffic is left over.

Where the server sees it from

The server also sees network addresses. Members fetching group state, receiving pushes or sending messages arrive from IP addresses, and two accounts using the same home connection are linkable without any group at all. Push notification services add another party that sees a delivery event.

So the honest threat model has several observers: the message server, the push provider, anyone on the network path, and anyone who can compel logs from them. Each sees a slice of the picture.

What you can and cannot do about it

Mitigations exist, and each costs something:

  • Padding and batching blurs timing and size, at the price of latency and bandwidth.
  • Cover traffic adds fake deliveries, which burns battery on phones.
  • Mixnets or onion routing hide the network address, but add delay that chat users notice quickly.
  • Client-side fan-out through a relay you control moves the trust rather than removing it.

Without something like these, a server that wants the membership graph can compute it from delivery alone. A server that promises not to is making a policy claim, not a cryptographic one.

A fair way to read the claim

"The server does not store who is in your group" is true and useful. It means a stolen database is less damaging, and a request for stored data returns less. That matters.

It does not mean the server is blind during operation. If your threat model includes a server operator or anyone with access to its live logs, assume the recipient sets are visible. If it only includes a database leak, encrypted group state does most of what you want.

This applies beyond one app. Any messenger that routes through a central server faces the same trade: the thing that makes delivery work is the thing that leaks the graph.