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

  1. Inspect the logged u.Host — if it is not amazonaws.com, treat the request as malicious and drop it (do not retry).
  2. If you legitimately operate in an uncovered AWS partition (GovCloud/China), extend sesRegCertURL to allow the correct Amazon host suffix.
  3. Confirm the SNS subscription is delivering genuine messages: cross-check the TopicArn and message with an AWS-side confirmation.
  4. 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

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

Related errors


AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01). Data as JSON: /api/errors/6b296c2eb866f8da. Report an issue: GitHub.