knadh/listmonk · error
invalid SNS certificate URL: %v
Error message
invalid SNS certificate URL: %v
What it means
getCert rejects certificate URLs that do not match the sesRegCertURL regular expression, which only allows Amazon SNS certificate hosts (sns.<region>.amazonaws.com). This is a security guard against SSRF and signature-forgery attacks where an attacker supplies their own SigningCertURL. The error reports the parsed URL's host.
Source
Thrown at internal/bounce/webhooks/ses.go:225
sign, err := base64.StdEncoding.DecodeString(n.Signature)
if err != nil {
return err
}
return cert.CheckSignature(x509.SHA1WithRSA, s.buildSignature(n), sign)
}
// getCert takes the SNS certificate URL and fetches it and caches it for the first time,
// and returns the cached cert for subsequent calls.
func (s *SES) getCert(certURL string) (*x509.Certificate, error) {
// Ensure that the cert URL is Amazon's.
u, err := url.Parse(certURL)
if err != nil {
return nil, err
}
if !sesRegCertURL.MatchString(certURL) {
return nil, fmt.Errorf("invalid SNS certificate URL: %v", u.Host)
}
// Return if it's cached.
s.mu.RLock()
c, ok := s.certs[u.Path]
s.mu.RUnlock()
if ok {
return c, nil
}
// Fetch the certificate.
resp, err := http.Get(certURL)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {View on GitHub (pinned to 670c01717d)
Solutions
- Inspect the logged u.Host — if it is not amazonaws.com, treat the request as malicious and drop it (do not retry).
- If you legitimately operate in an uncovered AWS partition (GovCloud/China), extend sesRegCertURL to allow the correct Amazon host suffix.
- Confirm the SNS subscription is delivering genuine messages: cross-check the TopicArn and message with an AWS-side confirmation.
- Keep the regex restrictive — never replace it with a permissive URL check.
Example fix
// before: partition hosts rejected sesRegCertURL = regexp.MustCompile(`^https://sns\.[a-zA-Z0-9-]+\.amazonaws\.com/`) // after: allow AWS partition variants sesRegCertURL = regexp.MustCompile(`^https://sns\.[a-zA-Z0-9-]+\.amazonaws\.com(\.cn)?/`)
Defensive patterns
Strategy: validation
Validate before calling
func isAmazonSNSCertURL(certURL string) bool {
re := regexp.MustCompile(`^https://sns\.[a-zA-Z0-9-]+\.amazonaws\.com(\.cn)?/`)
return re.MatchString(certURL)
}
// before calling the webhook:
if !isAmazonSNSCertURL(notif.SigningCertURL) {
return errors.New("untrusted SigningCertURL: " + notif.SigningCertURL)
} Type guard
func isTrustedSNSNotification(n sesNotif) bool {
u, err := url.Parse(n.SigningCertURL)
return err == nil && u.Scheme == "https" &&
(strings.HasSuffix(u.Host, ".amazonaws.com") || strings.HasSuffix(u.Host, ".amazonaws.com.cn"))
} Try / catch
if err := ses.ProcessBounce(notif); err != nil {
if strings.Contains(err.Error(), "invalid SNS certificate URL") {
// untrusted/malformed URL: drop the request, alert on possible spoofing
log.Warn("rejected notification with non-Amazon cert URL", "err", err)
http.Error(w, "forbidden", http.StatusForbidden)
return
}
http.Error(w, "bad request", http.StatusBadRequest)
} Prevention
- Never disable or loosen the cert URL hostname check
- If using AWS GovCloud/China, explicitly update the allow-list regex for those partitions
- Alert on occurrences — a spike indicates someone probing your webhook
- Cross-check the notification's TopicArn against your known subscription ARNs
When it happens
Trigger: ProcessSubscription or ProcessBounce receives a notification whose SigningCertURL host is not an Amazon SNS domain — e.g. an attacker-crafted webhook, a test fixture pointing at a local URL, or an SES/SNS setup in a non-standard partition (sns.cn-north-1.amazonaws.com.cn, GovCloud, isolated regions) the regex doesn't cover.
Common situations: Spoofed webhook attempts (the guard working as intended); operating in AWS partitions the hard-coded regex doesn't recognize (China, GovCloud, FIPS endpoints); unit-test payloads with fake URLs; SNS message format changes moving the cert URL host.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- error getting SNS cert: %v
- 'email' column not found
- invalid e-mail address
- error unmarshalling SNS notification: %v
- globals.messages.invalidFields
AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01).
Data as JSON: /api/errors/6b296c2eb866f8da.
Report an issue: GitHub.