With the relayer registry proposal well on its way towards being voted on (and probably accepted), I’ve been thinking a bit about how relayers impact security of TC users.
One of the big vulnerability vectors that users face due to how relayers currently work has to do with the fact that the UI connects to relayer servers directly, for obtaining parameters from the relayers, for submitting relay transactions to them, and checking the status of their withdrawal job.
Because users are connecting directly to relayers, any information that is available over that HTTP request can be logged and analyzed by anyone who has access to (or compromises) the relayers. This includes headers sent in the request, which might be highly specific to the user’s browser, and the network connection properties (IP address, IP route, reverse DNS, services on the user’s public IP, etc).
Something very important to note is that the UI doesn’t only connect users to the one relayer that their withdrawal goes through. It connects users to every relayer that’s registered, in order to obtain the
/status information for each relayer. This is done the moment that you click the “Withdraw” tab in the app.
The implication of this is that the moment that any user clicks the “Withdraw” tab, they disclose a bunch of potentially-identifying information to the entire set of relayers.
What this also means is that every relayer can tell, with some degree of certainty, which withdrawal corresponds to which UI session that connected to them. Because the UI doesn’t continuously poll the relayers for information, but instead only does so when the Withdraw tab is loaded (every time it’s clicked), there is a strong correlation between when a relayer sees a status check from a user, and any withdrawal transactions submitted to the network shortly thereafter.
Additionally, if any user clicks the Withdraw tab either before, or shortly after they make a deposit (exploring the app, checking what happens if they put their note string in), their deposit transaction is similarly able to be correlated to their status check request.
What exacerbates this is that not every user is connecting to Tornado.cash through a VPN or Tor. I would suspect that a minority are - and anyone who runs a relay could probably tell you what the prevalence is.
If a user makes the deposit and withdrawal transactions from the same IP address (or other header information), then the privacy guarantees of TC are lost.
Lastly, if a user is first opening the app with a non-proxied connection, and then enables their proxy, this vulnerability would affect them as well, since the issue is correlating status connections with deposit/withdrawal transactions in time.
The likely best mitigation for this problem would be for relayers to serve their status information over IPFS, instead of directly to users. Each relayer would have an IPNS address associated with it, and could pin their status and job information under that address. The UI would then poll that in the same way that it loads the app resources.
This wouldn’t solve the issue of users connecting directly to relayers to submit transactions, but at least it would limit the connection to the relayer that the user has selected.
If we wanted to go further than that, we would probably want to find a way to enable the UI to submit messages to relayers via IPFS pubsub. However, that becomes something of a “Now you have two problems” situation, since public IPFS gateways are usually read-only, so we’d need to maintain a node that can serve user messages back to relayers.