1. Securing registration email
  2. Hacking accounts through email
  3. The solution: a separate mailbox
  4. Implementation details

I've been running my own email server basically forever. Recently, I've been thinking about possible attack vectors against my personal email. There's of course a lot of private information in that email address, and if someone manages to compromise my email account, they will see a lot of personal information. That's somewhat worrisome, but there are possibly more serious problems to worry about.

TL;DR: if you can, create a second email address to register on websites and use stronger protections on that account from your regular mail.

Hacking accounts through email

Strangely what keeps me up at night is more what kind of damage an attacker could do to other accounts I hold with that email address. Because basically every online service is backed by an email address, if someone controls my email address, they can do a password reset on every account I have online. In fact, some authentication systems just gave up on passwords algother and use the email system itself for authentication, essentially using the "password reset" feature as the authentication mechanism.

Some services have protections against this: for example, GitHub require a 2FA token when doing certain changes which the attacker hopefully wouldn't have (although phishing attacks have been getting better at bypassing those protections). Other services will warn you about the password change which might be useful, except the warning is usually sent... to the hacked email address, which doesn't help at all.

The solution: a separate mailbox

I had been using an extension (anarcat+register@example.com) to store registration mail in a separate folder for a while already. This allows me to bypass greylisting on the email address, for one. Greylisting is really annoying when you register on a service or do a password reset... The extension also allows me to sort those annoying emails in a separate folder automatically with a simple Sieve rule.

More recently, I have been forced to use a completely different email alias (register@example.com) on some services that dislike having plus signs (+) in email address, even though they are perfectly valid. That got me thinking about the security problem again: if I have a different alias why not make it a completely separate account and harden that against intrusion. With a separate account, I could enforce things like SSH-only access or 2FA that would be inconvenient for my main email address when I travel, because I sometimes log into webmail for example. Because I don't frequently need access to registration mail, it seemed like a good tradeoff.

So I created a second account, with a locked password and SSH-only authentication. That way the only way someone can compromise my "registration email" is by hacking my physical machine or the server directly, not by just bruteforcing a password.

Now of course I need to figure out which sites I'm registered on with a "non-registration" email (anarcat@example.com): before I thought of using the register@ alias, I sometimes used my normal address instead. So I'll have to track those down and reset those. But it seems I already blocked a large attack surface with a very simple change and that feels quite satisfying.

Implementation details

Using syncmaildir (SMD) to sync my email, the change was fairly simple. First I need to create a second SMD profile:

if [ $(hostname) = "marcos" ]; then
    exit 1
fi

SERVERNAME=smd-server-register
CLIENTNAME=$(hostname)-register
MAILBOX_LOCAL=Maildir/.register/
MAILBOX_REMOTE=Maildir
TRANSLATOR_LR="smd-translate -m move -d LR register"
TRANSLATOR_RL="smd-translate -m move -d RL register"
EXCLUDE="Maildir/.notmuch/hooks/* Maildir/.notmuch/xapian/*"

Very similar to the normal profile, except mails get stored in the already existing Maildir/.register/ and different SSH profile and translation rules are used. The new SSH profile is basically identical to the previous one:

# wrapper for smd
Host smd-server-register
    Hostname imap.anarc.at
    BatchMode yes
    Compression yes
    User register
    IdentitiesOnly yes
    IdentityFile ~/.ssh/id_ed25519_smd

Then we need to ignore the register folder in the normal configuration:

diff --git a/.smd/config.default b/.smd/config.default
index c42e3d0..74a8b54 100644
--- a/.smd/config.default
+++ b/.smd/config.default
@@ -59,7 +59,7 @@ TRANSLATOR_RL="smd-translate -m move -d RL default"
 # EXCLUDE_LOCAL="Mail/spam Mail/trash"
 # EXCLUDE_REMOTE="OtherMail/with%20spaces"
 #EXCLUDE="Maildir/.notmuch/hooks/* Maildir/.notmuch/xapian/*"
-EXCLUDE="Maildir/.notmuch/hooks/* Maildir/.notmuch/xapian/*"
+EXCLUDE="Maildir/.notmuch/hooks/* Maildir/.notmuch/xapian/* Maildir/.register/*"
 #EXCLUDE_LOCAL="$MAILBOX_LOCAL/.notmuch/hooks/* $MAILBOX_LOCAL/.notmuch/xapian/*"
 #EXCLUDE_REMOTE="$MAILBOX_REMOTE/.notmuch/hooks/* $MAILBOX_REMOTE/.notmuch/xapian/*"
 #EXCLUDE_REMOTE="Maildir/Koumbit Maildir/Koumbit* Maildir/Koumbit/* Maildir/Koumbit.INBOX.Archives/ Maildir/Koumbit.INBOX.Archives.2012/ Maildir/.notmuch/hooks/* Maildir/.notmuch/xapian/*"

And finally we add the new profile to the systemd services:

diff --git a/.config/systemd/user/smd-pull.service b/.config/systemd/user/smd-pull.service
index a841306..498391d 100644
--- a/.config/systemd/user/smd-pull.service
+++ b/.config/systemd/user/smd-pull.service
@@ -8,6 +8,7 @@ ConditionHost=!marcos
 Type=oneshot
 # --show-tags gives email counts
 ExecStart=/usr/bin/smd-pull --show-tags
+ExecStart=/usr/bin/smd-pull --show-tags register

 [Install]
 WantedBy=multi-user.target
diff --git a/.config/systemd/user/smd-push.service b/.config/systemd/user/smd-push.service
index 10d53c7..caa588e 100644
--- a/.config/systemd/user/smd-push.service
+++ b/.config/systemd/user/smd-push.service
@@ -8,6 +8,7 @@ ConditionHost=!marcos
 Type=oneshot
 # --show-tags gives email counts
 ExecStart=/usr/bin/smd-push --show-tags
+ExecStart=/usr/bin/smd-push --show-tags register

 [Install]
 WantedBy=multi-user.target

That's about it on the client side. On the server, the user is created with a locked password the mailbox moved over:

adduser --disabled-password register
mv ~anarcat/Maildir/.register/ ~register/Maildir/
chown -R register:register Maildir/

The SSH authentication key is added to .ssh/authorized_keys, and the alias is reversed:

--- a/aliases
+++ b/aliases
@@ -24,7 +24,7 @@ spamtrap: anarcat
 spampd: anarcat
 junk: anarcat
 devnull: /dev/null
-register: anarcat+register
+anarcat+register: register

 # various sandboxes
 anarcat-irc: anarcat

... and the email is also added to /etc/postgrey/whitelist_recipients.

That's it: I now have a hardened email service! Of course there are other ways to harden an email address. On-disk encryption comes to mind but that only works with password-based authentication from what I understand, which is something I want to avoid to remove bruteforce attacks.

Your advice and comments are of course very welcome, as usual

You said advice welcome, so

I would suggest chmod 0700 ~register/Maildir/, assuming it wasn't already non-world-readable.

(I checked my own mail server and saw that some subfolders inside my ~/Maildir were world-readable for some reason. No idea why -- should I blame dovecot? My own backup/restore procedures? Anyway it's not too scary: the main ~/Maildir is 0700. But that protection goes away when you mv it elsewhere.)

Comment by Marius Gedminas
good advice

That's good advice! As it turns out, the directory was already world/group unreadable, but that's always a good thing to check. :)

Dovecot should definitely do the right thing here. If it doesn't, that's a serious bug, in my opinion. That folders within the Maildir folder are world-readable is problematic as well, but less so if the parent directory is not executable.

It's possible backup/restore procedure mess with your permissions. Some software, like git for example, are quite bad at handling read-write permissions (git only tracks the executable bit). But stuff like tar and most other backup software should handle this correctly. rsync is an interesting exception in that, by default, it doesn't synchronize mode and you need to give it a flurry of flags (or just -a or is it -aA or maybe -aAH? ha ha! you never know!) to have it do the right thing.

Finally, mv should be safe. While cp doesn't preserve mode by default (that's what -p is for), mv does preserve mode, even when moving across devices, strangely.

Comment by anarcat
Created . Edited .