oxen-io / oxen-storage-server

Storage server for Oxen Service Nodes
MIT License
28 stars 48 forks source link

Ring Token as Server-side User ID: Enhancing Privacy by Not Persistently Storing Session Identity Public Keys in Oxen Storage Server #495

Open venezuela01 opened 6 months ago

venezuela01 commented 6 months ago

Edit: fix some terminology usage.

Currently, the owned_message table in the Oxen storage server uses the Session identity public key as a virtual user ID and stores it persistently.

This design has a disadvantage: in the event of an Oxen storage server breach, an attacker can retrieve a massive amount of Session IDs and potentially spam those Session IDs, among other actions that threaten privacy.

It may not be necessary to use the Session identity public key directly as the server-side user ID. We have a deterministic partitioner that translates Session identity public keys into a ring token (also referred to as swarm space keys in Session's terminology). In theory, we can use this ring token as the server-side user ID, or a virtual user ID, and always translate from the Session identity public key to ring tokens on the fly whenever there is a read/write request from any Session user.

For instance:

Assume a user has the Session ID 05a0962389777624bbe97f883221e5c03c967003e2af53e1c2d02028c1ff901224. Instead of removing the 05 prefix from the Session ID and using the public key 0xa0962389777624bbe97f883221e5c03c967003e2af53e1c2d02028c1ff901224 as the server side user ID, we could consider using the ring token 0xfb9809806501761 directly to store or retrieve user messages in the Oxen storage server.

In other words, we only need to maintain the connection between the Session ID and its ring token in memory and temporarily during the read/write operation. The rest of the time, the service node has no knowledge about how to reverse a ring token back to the original Session ID.

If we avoid saving Session identity public keys on disk, then Session users will not risk exposing their Session IDs even in the event of a data breach, since nobody can decipher a Session ID from its ring token.

For readers who are less familiar with the partitioning design of the Oxen storage server, below is a replicated implementation of the partitioner used by the Oxen Storage Server:

import struct

def pubkey_to_swarm_space_position(pk: str) -> int:
    if len(pk) != 64:
        print('Incorrect pubkey length')
        return
    res = 0
    bytes_pk = bytes.fromhex(pk)
    for i in range(4):
        buf, = struct.unpack('Q', bytes_pk[i*8:(i+1)*8])
        res ^= buf
    return struct.unpack('!Q', struct.pack('Q', res))[0]
KeeJef commented 6 months ago

Wouldn't the attacker just dump the memory to get access to the mapping between ring token and Session ID? Or are you assuming an attacker with only access to the disk and not memory, what type of attacker would this be?

venezuela01 commented 6 months ago

Wouldn't the attacker just dump the memory to get access to the mapping between ring token and Session ID? Or are you assuming an attacker with only access to the disk and not memory, what type of attacker would this be?

No, the memory mapping lasts only for a few hundreds milliseconds during a specific user's read/write requests on their messages; it won't be cached. The next time another read/write request occurs, the Session ID <-> Ring Token mapping need to be generated again.

For a specific user, 99% of the time he is not reading or writing anything. With the proposed design, if an attacker hacks a server, they will have no knowledge of the Session ID <-> Ring Token mapping from the past 30 days. If the attacker wants to know the Session IDs of all users on the server, the only thing they can do is to start monitoring user activity for the next 30 days. This gives the storage server operator significant time to observe anomalies on the server and take action.

In the current design, where the Session identity public key is used as the server-side user ID, an attacker can easily obtain Session IDs from the past 30 days, which only takes a few minutes of file copying, without the need for continuous monitoring of future traffic.

venezuela01 commented 6 months ago

Let's assume a state agent discovers a zero-day vulnerability in the Oxen storage server, and 100% of the servers are using the vulnerable version. Then, with the current design, the state agent can simultaneously connect to all 2000 servers and copy all the Session user IDs on the entire network, which takes only about 20 minutes.

Five hours later, one operator notices that his server seems to have been hacked and then shares his discovery with the entire operator community. More operators realize that their servers might have been hacked as well. However, by that time, there is little we can do to prevent the existing data breach; it has already occurred.

However, with the proposed design, even if the state agent simultaneously connects to all 2000 servers using the zero-day exploit, he would still need to patiently start monitoring future traffic. If, five hours later, one operator discovers that his server was hacked, then he can warn all other operators, and we all can take emergency actions. As a result, the attacker might only be able to recover less than 1% of the Session IDs, which might not be worth wasting a zero-day exploit on.

venezuela01 commented 6 months ago

Also, note that if a user enables fast push notifications, the OPTF's Session Push Notification Server (SPNS) will need to connect the user's Session ID with the user's APNS/Firebase ID. Since OPTF caches a large number of Session IDs, it is a potential single point of failure if an attacker hacks SPNS. If SPNS only saves users' ring tokens, then the privacy concern will be less significant.

venezuela01 commented 5 months ago

The recent exposure of the xz backdoor demonstrate the potential risk of 0-day exploits can unexpectedly affect Oxen service nodes.

Adopting the 'Ring Token as Server-side User ID' design can significantly reduce this risk. This approach ensures no cached Session IDs have been stored on compromised servers, preventing attackers from easily harvesting them. Instead, attackers must continuously monitor servers after a breach to incrementally collect Session IDs. With this protection in place, service node operators, alerted within a few hours to a couple of days about the breach, can start to inspect, clean, and strengthen their systems. This minimizes the potential damage of an exploit.