Skip to main content

SecureDrop keeps themselves busy reinventing the wheel

· 8 min read

I've said it already so I'll just rephrase: it takes three PhDs to use SecureDrop - one admin, one journalist and one user.

But you don't know what you don't know, so instead of fixing the complicated mess that (current) SecureDrop is - which could be done by including any viable open source decentralized messenger into their packaged solution - they're coming up with a new protocol of their own. Of course.

What does it do?

What is implemented here is a small-scale, self-contained, anonymous message box, where anonymous parties (sources) can contact and receive replies from trusted parties (journalists).

Where's the server?

Server: For this project, a server might be a physical dedicated server housed in a trusted location, a physical server in an untrusted location, or a virtual server in a trusted or untrusted context. Besides the initial setup, all the connections to the server have to happen though the Tor Hidden Service Protocol.

Fine, I'll just set up my own Hidden Service.

But, does Source need a PhD?

Source(s): No on-device persistence shall be required for a source to interact with the system; they should be able to conduct all communications using only a single, theoretically-memorizable passphrase. The source uses Tor Browser to preserve their anonymity.

Just using Tor Browser properly is very hard. Haven users don't need to use Tor, but they may if they wish. Because with Haven, Tor is optional so it's much harder to use Tor incorrectly if either party does choose to use it for secure direct messaging.

I'd even say that modern messaging apps like xx Network's Haven almost cannot be used incorrectly. Choose a strong password and don't have a spyware infested browser or OS. That's basically it.

But this is SecureDrop, so here we go again... Start by subscribing to Udemy's "Python from Zero to Hero"...

# python3 source.py -h
usage: source.py [-h] [-p PASSPHRASE] -a {fetch,read,reply,submit,delete} [-i ID] [-m MESSAGE] [-f FILES [FILES ...]]

options:
-h, --help show this help message and exit
-p PASSPHRASE, --passphrase PASSPHRASE
Source passphrase if returning
-a {fetch,read,reply,submit,delete}, --action {fetch,read,reply,submit,delete}
Action to perform
-i ID, --id ID Message id
-m MESSAGE, --message MESSAGE
Plaintext message content for submissions or replies
-f FILES [FILES ...], --files FILES [FILES ...]
List of local files to submit

How it could be done

Pull method

In this approach Source shares files over a Tor Hidden Service and informs a Journalist to download the file.

This doesn't require any development.

  • Journalist publishes their Haven nick (e.g. MrTriggerHappyJourno)
  • Source runs owns Haven container or accesses other party's (e.g. public) Haven instance to create their own identity (DrDeepThroat)
  • Source initiates chat with Journalist using Haven by contacting them directly and inviting them to personal chat channel with DrDeepThroat and MrTriggerHappyJourno as the only participants
  • If the Source decides to share their files, they start Onion container with Hidden (Web) Service and share the link to the Journalist in Haven private chat

I provide a walk-through and sample configuration files below.

Push method

This approach follows SecureDrop's approach where the "file upload" site is operated by Journalist. That seems like a very risky idea to me - Sources would better off uploading files to one of decentralized S3 services - but that's how SecureDrop does it.

In this approach they would still use a decentralized secure messenger (Haven from xx Network or some other) and the same Web server app Secure Drop already has.

Walk-through for Pull method

I think it's easier to use Haven and Pull method than SecureDrop (Push method) and I'll show you why.

With Docker installed, the Source downloads this repository from Tor Browser or over SOCKS5 proxy (using git cli) and starts all the services they'll need.

git clone https://github.com/armchairancap/xx-haven-container
cd xx-haven-container/tor
docker compose up

Haven is now running on localhost (temporary VM) and Caddy provides HTTPS proxy at https://localhost:443 to Source. This takes care of secure messaging and metadata shredding.

If you want to know more, see this guide or fire up your own instance.

This list below shows all services in this Docker Compose.

The configuration files can be in line with best practices and thereby require zero configuration by either Source (they use Haven and Web server) or Journalist (they use own instance of Haven).

  • Caddy - HTTPS proxy for Haven
  • NGINX - Web server that runs my Tor Hidden (Web) Service
  • Haven - local (personal) instance of Haven (to create Haven identity and chat with Journalist)
  • Alpine/Tor - Tor server that exposes Hidden (Web) Service at a .onion address
$ docker ps -a
CONTAINER ID IMAGE
52b873713f26 caddy
324711b42905 nginx:latest
5e3054ec8d21 ghcr.io/armchairancap/haven:latest
023a0df1a5ed alpine:latest

Tor is running in Alpine and the Source can get our Hidden Service address from Container ID for alpine:latest like so:

$ docker exec -it 023a0df1a5ed cat /var/lib/tor/hidden_service/hostname
6x366z3rjmkx4ebtlpbq2hyoqng2yzy46zjtivydo4xovfj5xbk7zuid.onion
# alternatively
$ sudo cat tor-service/hostname

The hostname file is in the tor-service directory (relative to repo clone location: ./xx-haven-container/tor/tor-service/).

That's all it takes to visit the service on Tor - use Tor Browser to access that link.

But, what is our NGINX service sharing/serving?

The two test files I put in the tor-docs subdirectory in the place where I run docker compose up:

$ sudo ls  ./tor-docs/
file.txt index.html

Actually, index.html isn't even necessary - it's there to make this demo simpler - by being able to view index.html which links to file.txt.

In any case, our Source shares 6x366z3rjmkx4ebtlpbq2hyoqng2yzy46zjtivydo4xovfj5xbk7zuid.onion with his Journalist and that's it.

The Journalist downloads the file over Tor and the Source can see that in Tor logs (in Docker Compose console).

After the Journalist confirms they got the file in Haven chat channel, the Source uses CTRL+C to stop Docker service and removes everything (the entire ./xx-haven-container/ directory or - even better - the entire temporary VM if they had one).

warning

Don't perform this step if you want to reuse your Onion address or Docker Compose example. (Of course, you may, and create another set of Tor and Haven identities next time you use this stack.)

# press CTRL-C to stop docker-compose
# sudo rm -rf ./tor-*

With Tor identity removed, next time the Sources wishes to use the same stack they have a new Onion address while Haven identity could be restored if they backed it up (by exporting it from Haven Web UI to JSON file) and stored in a secure location.

Back to those file names: our Source could share a direct link to the file (6x366z3rjmkx4ebtlpbq2hyoqng2yzy46zjtivydo4xovfj5xbk7zuid.onion/file.txt) and file.txt could be encrypted if you wanted the extra security - the Source may share that password with the Journalist in their private Haven chat channel.

The password could be shared with the Journalist in Haven, but if the file is randomly named there's no risk of anyone guessing this file name on a Hidden Service address with directory listing disabled (it is disabled by default on NGINX).

To take that approach, remove index.html and move the content file to a random name:

# use CTRL+C to stop docker compose
$ openssl rand -hex 50
cd7457899fcd7ea28f09b8fd7eec090f9207634501ee910c3a58b3b7bb35e68e8ec17b05a7e1626059a089cd7318ec69589a
$ sudo rm ./tor-docs/index.html
$ sudo mv ./tor-docs/file.txt ./tor-docs/cd7457899fcd7ea28f09b8fd7eec090f9207634501ee910c3a58b3b7bb35e68e8ec17b05a7e1626059a089cd7318ec69589a
$ docker compose start

All that's necessary here is to put your content in ./tor-docs/. There's no configuration of any kind. For "production" purposes a project could further improve configuration files included, of course.

  • caddy_config/ - auto-configured
  • caddy_data/ - auto-configured
  • Caddyfile - 3-line template file for reverse proxy on localhost, preconfigured
  • docker-compose.yml - Docker Compose file, preconfigured
  • tor-config/ - Tor Hidden Service configuration file (for NGINX), preconfigured
  • tor-docs/ - file(s) shared over Hidden Service (file.txt, etc.)
  • tor-service/ - Tor Hidden Service identity file (hostname, pub/priv key) - auto-generated if missing

Now, this isn't "more secure" than their latest PoC, but is it worse?

It took me two hours to put it together without writing any code and anyone who uses this is much less likely to make a mistake while using this approach.

Find my PoC configuration files here. (Credits: I used hidden-service-docker for Tor and NGINX.)

Summary

SecureDrop wants to reinvent their own wheel despite the fact that there are several good decentralized, secure messengers.

Normally, secure drops start with secure messaging and they'd be better off if they simply picked one of existing options.

Once that's taken care of, file upload/download is no longer difficult when done on Onion network.

I wouldn't say these are "solved problems", but the point here isn't that xx Network's Haven "solved" the problem of secure messaging (maybe it did, maybe it didn't - see for yourself). It is that SecureDrop very likely can't solve it better than other decentralized messengers.

They ought to stop dreaming about their own "protocol" and do what they're supposed to do which is making it easy to share files in private.