Tencent/WeKnora · error · ErrSSRFRedirectBlocked

%w: %w

Error message

%w: %w

What it means

When a redirect target is not on the SSRF whitelist, it is run through validateURLForSSRFForOutbound; if that fails (private IP, blocked host, etc.) the policy returns ErrSSRFRedirectBlocked wrapped with the underlying validation error ('%w: %w'), so both the sentinel and the cause are matchable via errors.Is.

Source

Thrown at internal/utils/security.go:728

		// Strip credentials when the redirect crosses hosts so connector
		// tokens (e.g. Yuque X-Auth-Token) cannot leak to a third party.
		if len(via) > 0 && !sameHTTPOrigin(via[0].URL, req.URL) {
			stripRedirectSensitiveHeaders(req)
		}

		// Validate the redirect target URL for SSRF (whitelist-aware).
		// Even whitelisted hosts must use http/https to prevent scheme-based attacks.
		redirectScheme := strings.ToLower(req.URL.Scheme)
		if redirectScheme != "http" && redirectScheme != "https" {
			return fmt.Errorf("%w: invalid scheme %s", ErrSSRFRedirectBlocked, redirectScheme)
		}
		redirectHost := req.URL.Hostname()
		if redirectHost != "" && IsSSRFWhitelisted(redirectHost) {
			return nil
		}
		if err := validateURLForSSRFForOutbound(req.URL.String()); err != nil {
			return fmt.Errorf("%w: %w", ErrSSRFRedirectBlocked, err)
		}

		return nil
	}
}

// SSRFValidatingRoundTripper enforces the URL policy for every outbound
// request, including URLs discovered at runtime by SDKs (for example OAuth
// metadata) that never passed through an application handler. Dial-time checks
// remain necessary to pin DNS answers and cover transports that cannot accept
// this wrapper directly.
type SSRFValidatingRoundTripper struct {
	Base http.RoundTripper
}

func (t *SSRFValidatingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	if req == nil || req.URL == nil {
		return nil, fmt.Errorf("outbound request blocked: request URL is required")

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Handle with errors.Is(err, secutils.ErrSSRFRedirectBlocked); log the wrapped cause for the specific reason.
  2. Fix the upstream so it does not redirect to private/internal addresses.
  3. If the target host is legitimately required, add it to the SSRF whitelist after security review (whitelisted hosts bypass outbound validation).
  4. Resolve the public hostname instead of an internal one on the redirecting service.

Example fix

// before
resp, err := client.Do(req)
return err
// after
if errors.Is(err, secutils.ErrSSRFRedirectBlocked) {
    return fmt.Errorf("redirect target failed SSRF validation: %v", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-resolve and check the redirect target
ips, _ := net.LookupHost(host)
for _, ip := range ips { if isPrivate(ip) { return fmt.Errorf("redirect target %s is private", ip) } }

Type guard

func isRedirectSSRFBlocked(err error) bool { return errors.Is(err, secutils.ErrSSRFRedirectBlocked) }

Try / catch

if errors.Is(err, secutils.ErrSSRFRedirectBlocked) {
    // wrapped cause explains why: private IP, blocked host, etc.
    return fmt.Errorf("redirect failed SSRF policy: %w", err)
}

Prevention

When it happens

Trigger: Following a redirect whose Location URL fails validateURLForSSRFForOutbound — e.g. target resolves to loopback, RFC1918, or link-local addresses — while using newSSRFCheckRedirect.

Common situations: Open redirects on third-party APIs pointing at internal hosts, DNS rebinding attempts, or staging services that redirect to internal-only backends.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/2f3d1c3b883e43a1. Report an issue: GitHub.