Signed Email Addresses
Tags: email
Published: 2022-07-08 18:35:23

---
title: Signed Email Addresses
published: true
date: 2022-07-07 15:30:00 UTC
tags: Email
canonical_url: https://kevincox.ca/2022/07/07/signed-email-addresses/
---

<main style="--width:min(36em,100%);display:grid;grid-template-columns:1fr var(--width) 1fr;overflow:auto;-moz-tab-size:4;-o-tab-size:4;tab-size:4;padding:0"><p style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">I recently made the questionable decision of running my own mailserver. For now, I am outsourcing sending reputation as there are many easy ways to do that for near-zero cost, but I am in charge of the inbound.</p>
<p style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">This all started when Google decided that legacy free “Google Apps for Your Domain” was sucking customers away from their paid offering. I knew that this was coming and was mostly ready for it (other than Google Photos) so I started trying a variety of mail providers, but none fit the bill. It turns out that GMail is actually pretty good inbox provider. The biggest problem area I had with other providers was spam filtering. FastMail was pretty good but blocked some important messages. MXroute blocked both too much and too little. I almost went back to Google (the backlash convinced them to roll back their decision for “personal use”). However, in the end I decided that running my own mailserver can’t be that bad.</p>
<p style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">More on that setup in a future post but I want to talk about an interesting spam control technique that I have been envisioning for years. While simple, it isn’t quite simple enough to implement with the filtering capabilities that most providers offer. But with access to custom <a href="https://rspamd.com/" style="margin:0;box-sizing:border-box;text-decoration:underline;-webkit-text-decoration-color:grey;text-decoration-color:grey">Rspamd</a> rules it was a snap to implement.</p>
<aside style="box-sizing:border-box;grid-column:2;margin:.2em 1em .6em;padding:.4em;background:#333;color:#fff;box-shadow:4px 4px 8px #000"><p style="margin:0;box-sizing:border-box">Rspamd was actually performing excellently out of the box, this extra filtering is probably unnecessary. But since I had been considering the idea for years I wanted to put it into practice and see how well it worked.</p></aside><p style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">This is a form of task-specific email addresses. For example <a href="https://support.google.com/a/users/answer/9308648" style="margin:0;box-sizing:border-box;text-decoration:underline;-webkit-text-decoration-color:grey;text-decoration-color:grey">GMail allows sub-addressing with +</a>. So <code style="margin:0;box-sizing:border-box;white-space:pre-wrap;padding:.1em;border-radius:.3em;background:#000;border:1px solid #555;color:#fff">me+github@example</code> could be given to GitHub then I can easily organize my GitHub messages. If I do this for every account I create then if I start receiving spam I know who sold my info.</p>
<p style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">The main problem with this is that sub-addressing with <code style="margin:0;box-sizing:border-box;white-space:pre-wrap;padding:.1em;border-radius:.3em;background:#000;border:1px solid #555;color:#fff">+</code> is incredibly common. So a lot of tools and services will strip it. This means that all but the least sophisticated spammer will be able to “guess” your main email, or just randomize the stuff after the <code style="margin:0;box-sizing:border-box;white-space:pre-wrap;padding:.1em;border-radius:.3em;background:#000;border:1px solid #555;color:#fff">+</code> to bypass any block.</p>
<p style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">A common solution to this is full-blown email aliases. This way you have <code style="margin:0;box-sizing:border-box;white-space:pre-wrap;padding:.1em;border-radius:.3em;background:#000;border:1px solid #555;color:#fff">z2y8r3w3n@example</code> and any unknown address isn’t allowed. Now spammers can’t guess random addresses or strip the per-account info. There are many options for this such as a third-party service like <a href="https://relay.firefox.com" style="margin:0;box-sizing:border-box;text-decoration:underline;-webkit-text-decoration-color:grey;text-decoration-color:grey">Firefox Relay</a> and built-in features from email providers such as <a href="https://www.fastmail.help/hc/en-us/articles/4406536368911" style="margin:0;box-sizing:border-box;text-decoration:underline;-webkit-text-decoration-color:grey;text-decoration-color:grey">FastMail’s Masked Email</a>. These services are great, but the main problem is that you need to go and configure a new address every time you sign up for a new service. This usually isn’t too painful but it still requires a bunch of clicks.</p>
<p style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">My solution was signed addresses. This way generating email addresses is easy. It can be done with a browser extension or website without talking to any service or even having your device online. As I was thinking about this I saw <a href="https://blame.email" style="margin:0;box-sizing:border-box;text-decoration:underline;-webkit-text-decoration-color:grey;text-decoration-color:grey">blame.email</a> on <a href="https://news.ycombinator.com/item?id=31820502" style="margin:0;box-sizing:border-box;text-decoration:underline;-webkit-text-decoration-color:grey;text-decoration-color:grey">Hacker News</a>. So I decided to steal their algorithm so that I can take advantage of their frontend.</p>
<p style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">Their algorithm is pretty simple. It uses MD5 which is unfortunate (and means that you can’t use the browser <a href="https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest" style="margin:0;box-sizing:border-box;text-decoration:underline;-webkit-text-decoration-color:grey;text-decoration-color:grey"><code style="margin:0;box-sizing:border-box;white-space:pre-wrap;padding:.1em;border-radius:.3em;background:#000;border:1px solid #555;color:#fff">crypto.subtle.digest</code> API</a>) but the cryptography isn’t important here. The goal is to block naive automation, not motivated attackers. A <a href="https://en.wikipedia.org/wiki/Cyclic_redundancy_check" style="margin:0;box-sizing:border-box;text-decoration:underline;-webkit-text-decoration-color:grey;text-decoration-color:grey">CRC-32</a> would have been sufficient.</p>
<div style="box-sizing:border-box;padding:0;grid-column:2;grid-column:1/4;margin:0 0 .8em;display:grid;grid-template-columns:1fr var(--width) 1fr;background:#222;border-top:1px solid #444;border-bottom:1px solid #444;overflow-x:auto"><pre style="box-sizing:border-box;grid-column:2;margin:.4em 1rem"><code style="margin:0;box-sizing:border-box;white-space:pre-wrap;white-space:pre;background:none!important;color:#fff">name <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"github.com"</span>
salt <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"Sup3r S3cre+"</span>
<span style="margin:0;box-sizing:border-box;padding:0">hash</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> md5<span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">(</span><span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"</span><span style="margin:0;box-sizing:border-box;padding:0;color:#759ef0">$name</span><span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">+</span><span style="margin:0;box-sizing:border-box;padding:0;color:#759ef0">$salt</span><span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"</span><span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">)</span>.hex[0:8]
signed_address <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"</span><span style="margin:0;box-sizing:border-box;padding:0;color:#759ef0">$name</span><span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">-</span><span style="margin:0;box-sizing:border-box;padding:0;color:#759ef0">$hash</span><span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">@example"</span>
</code></pre></div>
<p style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">So I get out <code style="margin:0;box-sizing:border-box;white-space:pre-wrap;padding:.1em;border-radius:.3em;background:#000;border:1px solid #555;color:#fff">github.com-3ece8a38@example</code>.</p>
<p style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">Then I configure Rspamd to validate these signatures.</p>
<div style="box-sizing:border-box;padding:0;grid-column:2;grid-column:1/4;margin:0 0 .8em;display:grid;grid-template-columns:1fr var(--width) 1fr;background:#222;border-top:1px solid #444;border-bottom:1px solid #444;overflow-x:auto"><pre style="box-sizing:border-box;grid-column:2;margin:.4em 1rem"><code style="margin:0;box-sizing:border-box;white-space:pre-wrap;white-space:pre;background:none!important;color:#fff"><span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">local</span> <span style="margin:0;box-sizing:border-box;padding:0">rspamd_cryptobox_hash</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0">require</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"rspamd_cryptobox_hash"</span>
<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">local</span> <span style="margin:0;box-sizing:border-box;padding:0">md5</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0">rspamd_cryptobox_hash</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">.</span><span style="margin:0;box-sizing:border-box;padding:0">create_specific</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"md5"</span>

<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">local</span> <span style="margin:0;box-sizing:border-box;padding:0">known</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#999">{</span>
	<span style="margin:0;box-sizing:border-box;padding:0">abuse</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700;color:#ffb433">true</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span>
	<span style="margin:0;box-sizing:border-box;padding:0">blog</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700;color:#ffb433">true</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span>
	<span style="margin:0;box-sizing:border-box;padding:0">spam</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700;color:#ffb433">false</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span>
	<span style="margin:0;box-sizing:border-box;padding:0;color:#ccc;font-style:italic;white-space:pre-wrap">-- Others redacted ...</span>

	<span style="margin:0;box-sizing:border-box;padding:0;color:#ccc;font-style:italic;white-space:pre-wrap">-- Signed addresses that have been revoked.</span>
	<span style="margin:0;box-sizing:border-box;padding:0;color:#999">[</span><span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"spammer-a8bffde3"</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">]</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700;color:#ffb433">false</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span>
<span style="margin:0;box-sizing:border-box;padding:0;color:#999">};</span>

<span style="margin:0;box-sizing:border-box;padding:0">rspamd_config</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">.</span><span style="margin:0;box-sizing:border-box;padding:0">KEVINCOX_TO</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#999">{</span>
	<span style="margin:0;box-sizing:border-box;padding:0">type</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"prefilter"</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span>
	<span style="margin:0;box-sizing:border-box;padding:0">callback</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">function</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">(</span><span style="margin:0;box-sizing:border-box;padding:0">task</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">)</span>
		<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">local</span> <span style="margin:0;box-sizing:border-box;padding:0">to</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0">task</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">:</span><span style="margin:0;box-sizing:border-box;padding:0">get_principal_recipient</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">()</span>

		<span style="margin:0;box-sizing:border-box;padding:0;color:#ccc;font-style:italic;white-space:pre-wrap">-- Get just the user part, drop subaddress and domain.</span>
		<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">local</span> <span style="margin:0;box-sizing:border-box;padding:0">user</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0">to</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">:</span><span style="margin:0;box-sizing:border-box;padding:0">match</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"^[^+@]*"</span>
		<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">if</span> <span style="margin:0;box-sizing:border-box;padding:0">not</span> <span style="margin:0;box-sizing:border-box;padding:0">user</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">then</span>
			<span style="margin:0;box-sizing:border-box;padding:0;color:#ccc;font-style:italic;white-space:pre-wrap">-- If we can't identify a recipient add some spam score and quit.</span>
			<span style="margin:0;box-sizing:border-box;padding:0">task</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">:</span><span style="margin:0;box-sizing:border-box;padding:0">insert_result</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">(</span><span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700;color:#ffb433">true</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"KEVINCOX_TO_MISSING"</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#ffb433">2</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">.</span><span style="margin:0;box-sizing:border-box;padding:0;color:#ffb433">0</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span> <span style="margin:0;box-sizing:border-box;padding:0">user</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">)</span>
			<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">return</span>
		<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">end</span>

		<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">local</span> <span style="margin:0;box-sizing:border-box;padding:0">k</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0">known</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">[</span><span style="margin:0;box-sizing:border-box;padding:0">user</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">]</span>
		<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">if</span> <span style="margin:0;box-sizing:border-box;padding:0">k</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">==</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700;color:#ffb433">true</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">then</span>
			<span style="margin:0;box-sizing:border-box;padding:0;color:#ccc;font-style:italic;white-space:pre-wrap">-- A regular non-signed address.</span>
			<span style="margin:0;box-sizing:border-box;padding:0">task</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">:</span><span style="margin:0;box-sizing:border-box;padding:0">insert_result</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">(</span><span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700;color:#ffb433">true</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"KEVINCOX_TO_KNOWN"</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#ffb433">0</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">.</span><span style="margin:0;box-sizing:border-box;padding:0;color:#ffb433">0</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span> <span style="margin:0;box-sizing:border-box;padding:0">user</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">)</span>
		<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">elseif</span> <span style="margin:0;box-sizing:border-box;padding:0">k</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">==</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700;color:#ffb433">false</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">then</span>
			<span style="margin:0;box-sizing:border-box;padding:0;color:#ccc;font-style:italic;white-space:pre-wrap">-- Blocked (signed or unsigned) addresses.</span>
			<span style="margin:0;box-sizing:border-box;padding:0;color:#ccc;font-style:italic;white-space:pre-wrap">-- TODO: Forward to a spam reporting service.</span>
			<span style="margin:0;box-sizing:border-box;padding:0">task</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">:</span><span style="margin:0;box-sizing:border-box;padding:0">set_pre_result</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">(</span><span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"reject"</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"Confirmed Spammer"</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">)</span>
		<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">elseif</span> <span style="margin:0;box-sizing:border-box;padding:0">user</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">:</span><span style="margin:0;box-sizing:border-box;padding:0">find</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"-"</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">then</span>
			<span style="margin:0;box-sizing:border-box;padding:0;color:#ccc;font-style:italic;white-space:pre-wrap">-- If the username contains a `-` it must be signed.</span>

			<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">local</span> <span style="margin:0;box-sizing:border-box;padding:0">name</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span> <span style="margin:0;box-sizing:border-box;padding:0">hash</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0">user</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">:</span><span style="margin:0;box-sizing:border-box;padding:0">match</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"^(.*)-([^-]*)$"</span>
			<span style="margin:0;box-sizing:border-box;padding:0">md5</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">:</span><span style="margin:0;box-sizing:border-box;padding:0">reset</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">()</span>
			<span style="margin:0;box-sizing:border-box;padding:0">md5</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">:</span><span style="margin:0;box-sizing:border-box;padding:0">update</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">(</span><span style="margin:0;box-sizing:border-box;padding:0">name</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">..</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"+"</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">..</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"Sup3r S3cre+"</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">)</span>
			<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">local</span> <span style="margin:0;box-sizing:border-box;padding:0">expected</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">=</span> <span style="margin:0;box-sizing:border-box;padding:0">md5</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">:</span><span style="margin:0;box-sizing:border-box;padding:0">hex</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">():</span><span style="margin:0;box-sizing:border-box;padding:0">sub</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">(</span><span style="margin:0;box-sizing:border-box;padding:0;color:#ffb433">0</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#ffb433">8</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">)</span>
			<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">if</span> <span style="margin:0;box-sizing:border-box;padding:0">hash</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#f5a3a3;font-weight:700">==</span> <span style="margin:0;box-sizing:border-box;padding:0">expected</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">then</span>
				<span style="margin:0;box-sizing:border-box;padding:0;color:#ccc;font-style:italic;white-space:pre-wrap">-- Signed addresses skip the spam filter.</span>
				<span style="margin:0;box-sizing:border-box;padding:0">task</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">:</span><span style="margin:0;box-sizing:border-box;padding:0">set_pre_result</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">(</span><span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"accept"</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"Signed address."</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">)</span>
			<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">else</span>
				<span style="margin:0;box-sizing:border-box;padding:0">task</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">:</span><span style="margin:0;box-sizing:border-box;padding:0">set_pre_result</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">(</span><span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"reject"</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"Invalid recipient."</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">)</span>
			<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">end</span>
		<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">else</span>
			<span style="margin:0;box-sizing:border-box;padding:0;color:#ccc;font-style:italic;white-space:pre-wrap">-- Not known and not signed.</span>
			<span style="margin:0;box-sizing:border-box;padding:0">task</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">:</span><span style="margin:0;box-sizing:border-box;padding:0">insert_result</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">(</span><span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700;color:#ffb433">true</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#b3d733">"KEVINCOX_TO_UNKNOWN"</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span> <span style="margin:0;box-sizing:border-box;padding:0;color:#ffb433">1</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">.</span><span style="margin:0;box-sizing:border-box;padding:0;color:#ffb433">0</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span> <span style="margin:0;box-sizing:border-box;padding:0">user</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">)</span>
		<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">end</span>
	<span style="margin:0;box-sizing:border-box;padding:0;color:#8585e0;font-weight:700">end</span><span style="margin:0;box-sizing:border-box;padding:0;color:#999">,</span>
<span style="margin:0;box-sizing:border-box;padding:0;color:#999">}</span>
</code></pre></div>
<p style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">Ideally you would be able to reject everything that is unsigned, however the world isn’t perfect.</p>
<ol style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">
<li style="margin:0;box-sizing:border-box">I want some long-lived email addresses to give to friends.</li>
<li style="margin:0;box-sizing:border-box">This is an old domain, so I have given out various addresses in the past that I am not ready to revoke yet.</li>
</ol>
<p style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">As time goes on, and I collect more data I will slowly tighten the rules. For now everything gets slotted into one of the following categories.</p>
<ol style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">
<li style="margin:0;box-sizing:border-box">Known addresses: Allow, subject to regular spam checking.</li>
<li style="margin:0;box-sizing:border-box">Blocked addresses: Reject and report.</li>
<li style="margin:0;box-sizing:border-box">Valid signed addresses: Allow, skipping the spam filter.</li>
<li style="margin:0;box-sizing:border-box">Blocked patterns: Reject. (For example I reject all addresses that contain a <code style="margin:0;box-sizing:border-box;white-space:pre-wrap;padding:.1em;border-radius:.3em;background:#000;border:1px solid #555;color:#fff">-</code> or <code style="margin:0;box-sizing:border-box;white-space:pre-wrap;padding:.1em;border-radius:.3em;background:#000;border:1px solid #555;color:#fff">.</code> because no emails I have given out in the past used these characters. Any instances of these are guessed addresses.)</li>
<li style="margin:0;box-sizing:border-box">Others: Allow (for now), subject to increased spam checking.</li>
</ol>
<p style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">My setup uses a catchall domain, but it could trivially be modified to work with sub-addressing. Just use format such as <code style="margin:0;box-sizing:border-box;white-space:pre-wrap;padding:.1em;border-radius:.3em;background:#000;border:1px solid #555;color:#fff">me+$name-$hash@example</code>. Of course, it will be extra valuable to reject mail sent to just <code style="margin:0;box-sizing:border-box;white-space:pre-wrap;padding:.1em;border-radius:.3em;background:#000;border:1px solid #555;color:#fff">me@example</code> as stripping the detail will be common.</p>
<h2 id="conclusion" style="box-sizing:border-box;grid-column:2;margin:.4em 1rem .8em"><a href="https://kevincox.ca/2022/07/07/signed-email-addresses/#conclusion" style="margin:0;box-sizing:border-box;text-decoration:inherit;color:inherit!important;text-shadow:inherit!important;text-decoration:underline;-webkit-text-decoration-color:grey;text-decoration-color:grey">Conclusion</a></h2>
<p style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">Is this solution over-designed? Almost certainly. You can get most of the benefit without the signing. Just give out emails like <code style="margin:0;box-sizing:border-box;white-space:pre-wrap;padding:.1em;border-radius:.3em;background:#000;border:1px solid #555;color:#fff">github@example</code> and 99% of companies won’t try to guess other email addresses. If an address is sold, blocking that address will likely be enough. This gives you knowledge of who shared your email and decent blocking for little effort.</p>
<p style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">However, the fact that this system is reliable enough to skip the spam filter is also very beneficial. I no longer have to worry about an important message getting lost in the Junk folder as these signed addresses ensure that I get everything from these privileged senders (until I revoke their privileges).</p>
<p style="box-sizing:border-box;margin:0 1rem .8em;grid-column:2">Another minor benefit of this system is that it reduces load on the spam filter. I am trying to get away with a very cheap server and 1GiB of RAM is tight for what I am running on it. Spam filtering gets slowed down by slow network requests and cold disk caches and was frequently taking 20-60s. Signed addresses skip 99% of the spam filter and consistently take under a second. This means that messages appear in my inbox noticeably faster which is a nice improvement.</p></main>