0

I'm writing a mailing list manager program. For subscribing and unsubscribing, I'm considering using HMACs of email addresses with secret keys to generate unsubscribe links. This key would be generated one time on the server and then used for all links from that time on. Links would have this format:

https://list.example.com/unsubscribe?address={email_address}&token={hmac(email_address, secret_key)}

When someone subscribes through my web form, my server will send an email with one of these links. When they click the link to confirm their subscription, I need to verify that my server actually sent it.

Adam Katz
  • 9,718
  • 2
  • 22
  • 44
Someone
  • 115
  • 4
  • On the server side, how would you process the token to find the email address that needs to be unsubscribed? HMAC's are (by design) not reversible - even if you know the secret key. Symmetric encryption (e.g. AES) might work better than an HMAC function for what you are trying to do. – mti2935 Jun 22 '22 at 17:21
  • @mti2935 – The email address is another parameter in the GET request, so it can be verified on the fly. – Adam Katz Jun 23 '22 at 02:55
  • The email address and HMAC are both included in the URL, and the server already has the key, so the server can check `hmac(email_address, secret_key) == hmac_from_url`. If that is `true`, then the link was generated by the server. – Someone Jun 23 '22 at 02:57

1 Answers1

1

First, it's good to spell out the problem that you're trying to solve. Then you can determine if an authentication token is necessary and what it needs for proper security.

To prevent user ennumeration attacks that might reveal if somebody is subscribed, make the server's response more vague (see that link).

I otherwise have no concerns about "malicious" unsubscribe actions (unless this is some kind of critical system, in which case: maybe you shouldn't be using email?). I think the easiest accidental unsubscribe comes from forwarded emails, which an auth token doesn't actually alleviate (consider 2FA instead). There is, of course, no harm in including an auth token anyway.

Another risk is that of list bombing, which an auth token would not help. Consider rate limiting, captchas, and/or 2FA to limit list bombs. While confirmed opt-in (COI) limits list bombs to a single message, most lists bombs are COI, so you need more protections or else your system will be abused and anti-spam services will block your mail.

Private or invitation-only lists/newsletters additionally need moderator approval for signups, which easily inserts itself into a COI process.

In the comments, it was established that this is primarily for COI links in order to prove that a subscription request is indeed desired by the subscribing email address (and approved by moderators when necessary). While I believe the HMAC solution is sound here, I'd personally just generate a random code instead. Store it (and the request timestamp) with the user's info in your database and remove the code when the user is verified. To limit clutter and potential abuse, regularly purge unconfirmed users whose timestamps are too old (48h and 7d seem appropriate times depending on your audience).

(If you're more interested in the cryptographic soundness of that concept, I suggest asking Cryptography Stack Exchange instead.)

Adam Katz
  • 9,718
  • 2
  • 22
  • 44
  • No, it isn't critical. I still need some kind of authentication for subscribing, right? I was mostly asking about the cryptographic soundness; is it possible to migrate the question? – Someone Jun 22 '22 at 20:19
  • You need authentication for subscribing, but I don't see any concern for *un*subscribing. Yes, we can migrate this to crypto.SE. I've made a vote to do so, but it needs a moderator or more votes to get it done. – Adam Katz Jun 22 '22 at 22:05
  • Okay, thank you! Should I update the question to ask if this is secure for subscribing? The only attack i can think of in that case is if someone forwards the subscription confirmation email to someone else (which i doubt anyone will), then unsubscribed, the person they forwarded it to could re-subscribe them. – Someone Jun 23 '22 at 00:58
  • To elaborate: you _might_ need auth codes for subscribing. If you design it right, they can prevent [list bombing](https://security.stackexchange.com/a/237400/42391 "I woke up and my email has been signed up to hundreds of online services. How can I fix this?"). An auth code might similarly prevent unauthorized signups to a private list. However, I don't see auth codes as ideal solutions to those problems. (Consider rate limiting, captchas, moderator approval, and/or 2FA.) Define your problems first, then see if your solution actually makes sense. – Adam Katz Jun 23 '22 at 02:53
  • This isn't a list that people can send to; it's a newsletter. – Someone Jun 23 '22 at 02:54
  • A newsletter is subject to the same concerns. List bombs are blowback; you're sending confirmation emails and newsletters to the attacker's specified victims (see my link). Private newsletters with carefully-maintained memberships exist as well. What problem(s) are you trying to solve with this auth code? – Adam Katz Jun 23 '22 at 02:58
  • When someone subscribes through a Web form, my server will send an email with one of these links. When they click the link to confirm their subscription, i need to verify that my server actually sent it. – Someone Jun 23 '22 at 02:59
  • I want to make sure a user cannot be subscribed (or unsubscribed) without their consent. – Someone Jun 23 '22 at 02:59
  • Ah, good on you for implementing [confirmed opt-in](https://en.wikipedia.org/wiki/Opt-in_email#Confirmed_opt-in_(COI)/Double_opt-in_(DOI)) (COI). You just want to ensure that the subscription is pending and the confirmation click comes from the subscribing email address. I'd personally just generate a random string, put it in your DB to denote the pending subscription, and copy it in the COI link. Don't forget to purge pending subscriptions after ~48h to limit abuse and to keep your DB clean. – Adam Katz Jun 23 '22 at 03:11
  • Okay, thank you! I'm assuming that technique could be applied to unsubscribing as well, even if it isn't really needed? – Someone Jun 23 '22 at 03:14
  • I have updated the question and my answer to incorporate all of the above comments. Given how this has evolved away from a question of cryptographic integrety, I have also retracted my `close` vote. If @Someone wants to ask specifically about that, they can do so in a fresh question on crypto.SE. Moderator: feel free to purge this and the above comments. – Adam Katz Jun 23 '22 at 04:23
  • Thank you! For a CAPTCHA, I really want to make sure the site is 100% FOSS and privacy-respecting (no connections to servers other than mine), and AFAICT all major CAPTCHA implementations are proprietary and phone home to the developer's server. Are questions about the topic of the list enough? – Someone Jun 23 '22 at 05:17
  • Great question about captchas, but that's a separate question that is likely better posted on Stack Overflow. Also consider [searching the web for "open-source captcha"](https://duckduckgo.com/?q=open-source+captcha). – Adam Katz Jun 23 '22 at 15:07