Programming, philosophy, pedaling.

Why don't we do email verification in reverse?

Aug 20, 2022     Tags: curiosity, programming    

This post is at least a year old.

This is a summary of a random thought I had on the subway.

Just about everybody who has ever used a service on the web has experienced the following account creation follow:

  1. Click “New account”,
  2. Enter a username and/or email and password,
  3. Log in, only to discover that the account is locked or limited pending “email verification.”

“Email verification,” in turn, means:

  1. Opening up a new browser tab or clicking on your email client,
  2. Waiting for an email to appear from the service,
  3. Finding the right “verification” URL in the email and clicking it,
  4. Being redirected to a splash page affirming that your email has been verified, and your account is fully usable.

In my experience, just about every step in the second list can fail in user-hostile ways:

All in all, I would describe the “normal” email verification flow as Kafkaesque: ambiguity and anxiety come in the forms of both time and space; unwritten rules must be followed and may or may not result in stated outcomes; I am considered a suspect (“unverified account”) until I have completed obscure and performative rituals.

So, the thought I had: why not do email verification “in reverse”? In other words, why not have the user email us?

Reverse, reverse

Here’s what the ordinary email verification process looks like, as an interaction flow between a handful of the components and entities involved3:

…and here’s what my proposed flow looks like:

Flow diagrams aren’t the most intuitive, so as a plain sequence of steps:

  1. Click “new account”,
  2. Enter an email,
  3. Click on the “verify my email” link, which opens up your email client with a pre-defined recipient, subject and body,
  4. Click “send”,
  5. Wait for the server to receive and verify it, making your account fully usable.

This has, in my eyes, a number of tangible benefits over the “normal” email verification flow:

  1. The user is more active: instead of waiting to receive an email in their inbox, the user is immediately presented with an email to send. They can make progress in the flow themselves, and can sanity-check the state of the verification by e.g. confirming that the email is in their outbox. Even if the flows take roughly the same amount of wall time, this activity makes the “reverse” flow feel faster and more responsive.

  2. The user has fewer opportunities to make mistakes: Users frequently mis-copy verification links, or use clients that mangle them, &c. These mistakes can’t happen in the “reverse” flow, because there’s no verification link to click. The user only has to remember how to send an email, which is a reasonable expectation in any scheme where the user is expected to have an email address.

  3. The user’s mistakes are easier to detect: If a user mis-enters their email address in the “normal” flow, they have no easy way to determine whether the verification email went to the wrong inbox or is simply delayed. The reverse flow can preempt this, by embedding a private, unique ID with each challenge email: if a received email has the ID but the wrong address, the web server can warn the user that they’ve provided the wrong address (and can display both without leaking information, since they’ve demonstrated knowledge of the wrong address and proved ownership of the correct one).

How does it work (securely)?

The “reverse” flow should make intuitive sense: we’re proving that the user controls the specified email address by challenging them to send us an email from it. This is in contrast to the normal flow, where our proof involves sending the user an email and challenging them to click the link contained within it. In both cases, the proof is the same: “email verification” means “the user controls an email address.”

So: how can we be confident that the reverse flow is at least as correct and secure as the normal flow? We need to impose several constraints.

Sending an email is not good enough

Email is notoriously spoofable: relays historically accepted messages from anybody, with no mechanism for distinguishing authentic senders from spoofed ones.

This is made worse by SMTP’s decoupling of envelope and presentation information for senders: an SMTP client specifies a return path address at the envelope layer via with MAIL FROM command, and subsequently specifies one or more presentation (i.e., email metadata) layer response addresses4.

Email providers realized that this wasn’t great, and have adopted three5 different (and separately scoped) standards for ensuring email authenticity:

Put together, these three standards represent the best commonly deployed email authenticity techniques. As such, they’re what we need to use for our “reverse” flow. Anything else would require users (and email providers) to make changes to accommodate us, which simply isn’t going to happen.

As for how we’d use them: we need SPF to provide envelope authenticity (i.e., ensuring that the IP that sends us an email is authorized to do so by the domain that it’s claiming to be from), and DKIM to provide message authenticity (i.e., that the header and body of the email are signed with key specified by the domain specified in the DKIM header.) Without these, we could not be confident that (1) the email is coming from the server(s) we expect, and (2) that the body of the email has not been tampered with by an intermediary.

I don’t think this scheme needs DMARC, since we don’t want the sending email server to have any control over our enforcement of SPF and DKIM policies: anything other than perfect SPF and DKIM availability and correctness must result in a failure.

Session and uniqueness considerations

On top of the (relatively weak) authenticity guarantees that DKIM and SPF provide provide, this scheme needs a couple of other components:

Is it a good idea?

I have no idea; it just popped into my head. I think it’s more user-friendly, and I’m pretty sure it’s at least as secure as the “normal” flow:

I could be convinced that it’s an unacceptable abuse of SPF and relies on a historically fragile URI scheme (mailto:), but my current perception is that the use of SPF here is more-or-less appropriate (and consistent with its intended use) and that the mailto: features required (recipient, subject, and body) are well-supported by the overwhelming majority of email clients. It’s a shame that SPF isn’t cryptographically bound in any way but, well, neither is DNS itself (and you’re certainly relying on that being correct to faithfully deliver your verification emails).

Some potential limitations and other thoughts:

  1. To be absolutely clear: Etsy is not a special target here and is not doing anything particularly wrong (relative to how everybody else does verification emails); they were just the first example I could find in my inbox. 

  2. Really, just Chrome. Firefox has “multi-account containers” for this purpose and they work excellently; they can even be scoped to domains to prevent a clicked link from opening in the wrong container. 

  3. I’ve simplified it substantially: I don’t think the distinction between the web frontend and backend or MTA/MDA matter for these purposes. 

  4. By my count, there are no less than three different headers consulted to present a final “From” to the user: Reply-To, Sender, and From itself. I’m not enough of a masochist to determine when and how the standard(s) dictate these should be prioritized, much less categorize how actual MTA and MUA implementations behave. 

  5. There are now even more, like ARC. But these are the most widely adopted for now, and the only ones that directly address our use case. 

  6. The headers that DKIM signs for are configurable, via the h=... field. 

  7. You can plow the earth with salt, but DNS will always remain. 

  8. Which is really just an alias for a TXT record. I think. 

  9. So that they can update their email server IP ranges without manual changes on my side. 

  10. SPF supports both “soft” and “hard” rejects, denoted by ~ and - in policy specifiers, respectively. The difference between the two isn’t important for our purposes.  2

  11. Reader: if this seems bad to you, it’s because it is. 

  12. This isn’t really a user error, since there are plenty of legitimate cases where multiple users might control an inbox (e.g. an invoices inbox). The goal with the nonce isn’t to prevent this, but to make sure that the server can distinguish each challenge and complete the verification for the right browser session. 

  13. Which is already not useful to an attacker who can’t control the inbox. 

  14. They’re effectively mandatory, if you want to avoid default filtering by the Google-Microsoft email duopoly. 

  15. To their credit, Google is no longer using tiny RSA keys for DKIM. To their discredit, they’re still using RSA. 

Discussions: Reddit Twitter