Secure Subscriptions


neon_data_rivers

Introduction

Up to this point, when I’ve wanted to share and promote a new blog post, I’ve only posted on LinkedIn. While this has been a helpful channel, it’s limited. It is not guaranteed to notify those who are interested. New LinkedIn posts can be easily missed, potentially lost in the noise of everything else, or simply superseded by newer posts when someone takes time to review the latest. While I will continue to share some posts on LinkedIn, I want something more reliably delivered. Something more direct.

I added an RSS feed, which can be useful to readers, but it’s not what most people prefer to leverage. It requires extra effort in setting up a means to read and subscribe. This is just enough friction to discourage most people. Moreover, RSS is obscure; most people don’t really know about it.

The ideal universal solution is email. To this end, I built a new email-based subscription service. Now people can easily sign up for email notifications to get notified about new blog posts. This approach is familiar, low-friction, and delivers an intuitive user experience.

Architecturally, the goal was to build a system that runs entirely at the edge, serverless, databaseless (at least, not self-managed), and enforces cryptographic trust at every step. Let’s run through it.

Architectural Approach

I set out to create a lightweight, easy to operate and manage, cost-effective (even free!), and secure solution. My requirements were simple - minimal infrastructure, or zero, depending on your perspective. It should use only existing API-first services I already use (e.g. Cloudflare, Resend, etc.).

The solution has to be secure. It must be resistant to abuse and prevent spam. Unwanted bots must be prevented from submitting email addresses or abusing the subscription endpoints. They must not be able to compromise service endpoints to send spam to subscribers or anyone for that matter. Humans with valid email addresses must verify their ownership of mailboxes. Of course, subscriber email addresses must not be publicly exposed.

Mitigating the Bots

To prevent bots from abusing the subscription service, Cloudflare Turnstile is used. When an actor visits the site, Turnstile observes various signals. If that actor appears to be human, Turnstile issues a token to their client. When the user submits their email address, this token is sent along with the request to the backend Worker, which verifies it against Cloudflare’s validation API. Upon submission, if the token is valid, and not expired, then the email verification flow will be triggered. Malicious actors attempting to hit the subscription endpoint directly will fail without a valid token.

Secure Flows with Web Crypto

Cryptography is our best friend here. There is no database required. When a user enters their email, a Cloudflare Worker generates a stateless verification token, using the Web Crypto API. The worker takes the email address and a timestamp and concatenates them into a message string. The worker signs this message with a secret key using the Web Crypto API, outputting a single, URL-safe cryptographic token containing the payload and its signature.

This unique string is sent to the user in a link pointing to the confirmation endpoint. When the user clicks the link, the worker decodes the information, verifies that the timestamp is less than 24 hours old, and verifies the signature using the secret key.

If the signature matches, the token is valid. Only then does the worker make a call to the Resend API to add the subscriber to the mailing list. Again, there’s no database to manage and no associated cleanup processes.

Subscription management is a two-way street. Unsubscription happens. When a user unsubscribes directly from an email, Resend processes the request and sends a webhook notification back to the Worker.

As with the subscription flow, the worker implements signature verification using Web Crypto. Resend signs the webhook payload and includes a timestamp and signature. This guarantees that the worker only processes legitimate unsubscribe events sent directly by Resend.

Protecting Broadcasts

When a new blog post is published, a GitHub Action triggers the broadcast endpoint to notify subscribers. To prevent malicious actors from triggering emails and spamming subscribers, the endpoint requires authentication. The request must include a webhook secret in the payload that matches the Worker’s environment variables. Without the secret, the Worker rejects the request.

Stopping Injection Attacks

While cryptography largely protects the subscription flows and service endpoints, we still have to secure the presentation layer. The system handles untrusted user input, like the subscriber’s email address. Therefore, any dynamically generated email notification could be a vector for HTML injection if rendered blindly by an email client. To mitigate this, the system enforces strict input validation, length limits, and output encoding before any data is processed or rendered.

Cloudflare’s free tier provides baseline managed rulesets that protect against major injection vulnerabilities, along with automated bot management. This can be helpful in stopping unsophisticated attacks and related shenanigans.

Wrap Up

Good security architecture is often about subtraction rather than addition. I avoided servers, databases, and open endpoints, and leveraged cryptographic verification and foundational application security controls. The resultant system is faster, cheaper, and inherently more secure.