tenant email spec: pin the SNS cert host to sns.<region>, not the amazonaws.com suffix
Adversarial review of the ingest endpoint defeated the check as specified. *.amazonaws.com includes every S3 bucket, API Gateway stage and Lambda URL, so any AWS customer can serve an attacker's PEM from a host with a genuine publicly-trusted certificate — reducing SNS signature security to the secrecy of a topic ARN, which is an identifier, not a secret. Adds the two requirements the original omitted: verify the fetched certificate against system roots and check its subject, and constrain SubscribeURL to the same host shape (review demonstrated it reaching link-local metadata). Also corrects this document's own label-awareness example, which was wrong: a bare HasSuffix rejects sns.eu-west-1.amazonaws.com.evil.com. The case it accepts is evilamazonaws.com.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 file changed,
+27
−5
docs/superpowers/specs/2026-08-19-tenant-email-design.md+27 −5
diff --git a/docs/superpowers/specs/2026-08-19-tenant-email-design.md b/docs/superpowers/specs/2026-08-19-tenant-email-design.md| index 01e0c6c9e..f2eda5eeb 100644 |
| --- a/docs/superpowers/specs/2026-08-19-tenant-email-design.md |
| +++ b/docs/superpowers/specs/2026-08-19-tenant-email-design.md |
| @@ -439,11 +439,33 @@ event destination onto a console endpoint (`POST /api/ses/events`). |
| Two security requirements, both non-negotiable: |
| - SNS **subscription confirmation** must be handled, or no events arrive. |
| -- The `SigningCertURL` **must be validated as an `amazonaws.com` host |
| - before it is fetched**. That check is the entirety of SNS signature |
| - security and it is the classic place to get it wrong: an unvalidated |
| - URL lets anyone who can reach the endpoint present their own signing |
| - certificate and forge every event below. |
| +- The `SigningCertURL` **must be pinned to SNS's own host before it is |
| + fetched** — `sns.<region>.amazonaws.com`, NOT the `amazonaws.com` |
| + suffix. **CORRECTED 2026-08-19 after review found the suffix form |
| + exploitable**: `*.amazonaws.com` includes every S3 bucket, API Gateway |
| + stage and Lambda URL, so any AWS customer can serve an attacker's PEM |
| + from a host with a genuine, publicly-trusted TLS certificate. With the |
| + suffix form, all that stands between a stranger and a forged event is |
| + the topic ARN — an identifier that appears in IaC and logs, not a |
| + secret. |
| + |
| +- **The fetched certificate must be verified against system roots and its |
| + subject checked.** Pinning the host without validating the chain still |
| + trusts whatever PEM comes back, and an open redirect under the allowed |
| + host would walk the fetch off AWS entirely. Neither check is sufficient |
| + alone; both are required. |
| + |
| +- **`SubscribeURL` must be constrained to the same host shape.** It is |
| + fetched after verification, so ordering is not the issue — an |
| + unconstrained URL is a blind SSRF that reaches link-local metadata, and |
| + review demonstrated it doing so. |
| + |
| +- A note on what label-awareness actually buys, because the obvious |
| + example is wrong: a bare `strings.HasSuffix(host, "amazonaws.com")` |
| + does NOT accept `sns.eu-west-1.amazonaws.com.evil.com` — that string |
| + ends in `evil.com`. The case it does accept is `evilamazonaws.com`, |
| + with no separating dot. That is the lookalike the `.`-prefixed check |
| + earns its keep against. |
| **Counts.** Events give per-app send, bounce, complaint and delivery |
| counts. Those counts are simultaneously the abuse signal and the **only** |