Skip to main content

Command Palette

Search for a command to run...

How to Prevent SSRF Attacks in ASP.NET Core APIs

Updated
โ€ข12 min read

Server-Side Request Forgery (SSRF) is one of the most underestimated vulnerabilities in modern web APIs โ€” and ASP.NET Core applications are not immune. SSRF prevention in ASP.NET Core is now a pressing enterprise concern, especially as APIs increasingly make outbound HTTP calls on behalf of users: fetching third-party content, validating webhook URLs, proxying file downloads, or enriching requests with external data. Each of those patterns is a potential SSRF entry point if the destination URL is not rigorously validated. The complete, production-hardened implementation for securing outbound requests โ€” including allowlist enforcement, DNS rebinding defence, and middleware integration โ€” is available on Patreon, where you'll find annotated source code that maps to what enterprise teams actually ship.

SSRF sits at position 10 in the OWASP Top 10 and has been the root cause of several high-profile cloud breaches where attackers leveraged the vulnerability to reach internal metadata endpoints, internal services, or cloud provider credential APIs. Understanding how to protect your ASP.NET Core APIs against SSRF is no longer optional โ€” it's table stakes for any team shipping internet-facing APIs. Understanding how security fits into the full production API picture โ€” alongside rate limiting, authentication, and resilience โ€” is exactly what Chapter 10 of the ASP.NET Core Web API: Zero to Production course covers, including how to apply defence-in-depth across the entire request pipeline.

What Is SSRF and Why Should ASP.NET Core Teams Care?

SSRF is an attack where an adversary tricks your server into making an HTTP request to an unintended destination โ€” typically an internal resource that is not publicly accessible. From the attacker's perspective, they are not calling your internal services directly. They are calling your API, which then calls the internal service on their behalf, returning the response.

In an ASP.NET Core API, the most common SSRF surfaces are:

  • User-supplied URLs passed to HttpClient โ€” webhook registration endpoints, file download proxies, image resize services, RSS feed fetchers
  • URL parameters used in redirects โ€” OAuth callback URLs, return URLs, redirect targets
  • Configuration-driven outbound calls โ€” APIs that accept a target URL in a request body and make a downstream call

The severity is compounded in cloud environments. AWS, Azure, and GCP all expose an Instance Metadata Service (IMDS) at a well-known internal IP address. A successful SSRF against an API running on a cloud VM can expose IAM credentials, bootstrap tokens, and environment secrets โ€” none of which are visible to the public internet, but all reachable via 169.254.169.254 or the cloud-specific equivalent.

Understanding the SSRF Attack Surface

Before writing any defence, it is worth mapping exactly what you are protecting. A vulnerable ASP.NET Core endpoint typically looks like this at a conceptual level: the controller receives a user-supplied URL, creates an HttpClient or HttpClientFactory instance, and forwards the request without validating the destination. That is the entire attack surface.

An attacker probing this endpoint will try several payloads in sequence:

  1. Direct internal IP access โ€” http://192.168.1.1/admin, http://10.0.0.1/api/internal
  2. Cloud metadata endpoints โ€” http://169.254.169.254/latest/meta-data/ (AWS), http://169.254.169.254/metadata/instance (Azure), http://metadata.google.internal/ (GCP)
  3. Localhost variations โ€” http://localhost/, http://127.0.0.1/, http://[::1]/
  4. DNS rebinding โ€” a domain the attacker controls that resolves to an internal IP after DNS resolution is cached
  5. Scheme abuse โ€” file:///etc/passwd, dict://, gopher:// โ€” non-HTTP schemes that bypass HTTP-only checks
  6. Redirect chains โ€” a public URL that issues a 301/302 to an internal destination after your blocklist has already been evaluated

Each of these requires a different layer of defence, which is why a single regex check against the URL string is never sufficient.

Defence Layer 1: Domain and IP Allowlisting (Not Blocklisting)

The most important architectural decision in SSRF defence is to always use allowlisting, never blocklisting. Blocklists fail. Attackers always find new ways to encode internal IPs โ€” decimal notation (http://2130706433/ for 127.0.0.1), IPv6 equivalents, octal, hexadecimal โ€” and any blocklist you build will eventually have a gap.

An allowlist works in the opposite direction: define exactly which destinations your API is permitted to call, and reject everything else. If your webhook registration endpoint is supposed to call customer-supplied URLs, your job is to clearly define what a "valid customer-supplied URL" looks like โ€” ideally a specific set of domains or at minimum a confirmed HTTPS endpoint.

In practice, an allowlist strategy at the application layer involves:

  • Resolving the target hostname to its IP addresses using Dns.GetHostAddressesAsync before making the outbound call
  • Verifying each resolved IP against a set of private/reserved CIDR ranges (RFC 1918, loopback, link-local, APIPA, and cloud metadata ranges)
  • Rejecting the request immediately if any resolved IP falls within a protected range

The DNS resolution step is critical โ€” it prevents the most common bypass where a hostname looks legitimate but actually resolves to an internal address. The check must be performed against all resolved addresses, not just the first, since some DNS responses return multiple records.

Defence Layer 2: DNS Rebinding Protection

DNS rebinding is the more sophisticated attack. The attacker registers a domain that initially resolves to a legitimate public IP, passes your validation, and then the DNS TTL expires and the domain re-resolves to an internal IP before your HttpClient actually makes the request. The gap between "check the IP" and "make the request" is the attack window.

Mitigating DNS rebinding in ASP.NET Core requires ensuring that the IP used for connection is the same IP you validated. The only reliable way to do this is to:

  1. Resolve the hostname to an IP address yourself
  2. Validate that IP
  3. Make the HTTP request using the resolved IP directly, passing the original hostname as the Host header

This bypasses the operating system's DNS resolver at connection time entirely, eliminating the rebinding window. While this requires more plumbing than a simple URL validator, it is the approach OWASP's Server-Side Request Forgery Prevention Cheat Sheet recommends for any high-security context.

Defence Layer 3: Disable Automatic Redirect Following

By default, HttpClient in .NET follows HTTP redirects automatically. This is almost always convenient โ€” and almost always wrong in an SSRF context. An attacker can register a public HTTPS endpoint that immediately issues a 302 redirect to an internal resource. Your IP allowlist checks the original URL, finds it clean, makes the request, and silently follows the redirect to the internal destination.

The fix is simple: set AllowAutoRedirect = false on the HttpClientHandler for any client that handles user-supplied URLs. If your use case genuinely requires following redirects, handle them manually โ€” validate each intermediate URL in the redirect chain against your allowlist before following it. This keeps the validation logic in your code, not in the framework's default behaviour.

For teams using IHttpClientFactory โ€” which is the recommended approach for HttpClient in ASP.NET Core โ€” this means registering a named client with a custom HttpClientHandler configured with AllowAutoRedirect = false.

Defence Layer 4: Scheme Enforcement

Your validators must explicitly check the URL scheme before any other validation. Only https:// (and possibly http:// in very specific, audited scenarios) should be accepted. Any other scheme โ€” file://, ftp://, dict://, gopher://, ldap:// โ€” must be rejected immediately, before DNS resolution even occurs.

This is a one-line check that prevents a surprisingly wide class of attacks, including attempts to read local files or invoke legacy protocol handlers that some operating systems expose.

In practice, this means your validator should:

  1. Parse the URL into a Uri object (using Uri.TryCreate with UriKind.Absolute)
  2. Immediately reject any URI where Scheme is not https (or your specific list of permitted schemes)
  3. Only proceed to DNS resolution if the scheme passes

Defence Layer 5: Wrapping It in ASP.NET Core Middleware

Rather than scattering URL validation logic across individual controllers and services, the cleanest enterprise approach is to implement an ISsrfValidator service and inject it wherever outbound HTTP calls are made. For high-risk surfaces โ€” such as a webhook registration endpoint โ€” this validation should be part of a dedicated service class, not inline in the controller.

The validator should be synchronous at the interface level but resolve DNS asynchronously using CancellationToken, so it plays well with ASP.NET Core's request cancellation model. Any validation failure should throw a domain exception (InvalidDestinationUrlException or similar) that your global exception handler catches and maps to a 400 Bad Request response โ€” never a 500, since this is user input validation, not an infrastructure failure.

This pattern also ensures that SSRF validation is a first-class, testable unit. You can write straightforward unit tests against the validator without involving HttpClient or the ASP.NET Core pipeline.

Defence Layer 6: Network-Level Egress Controls

Application-layer defences are necessary but not sufficient on their own. A defence-in-depth posture also requires network-level controls that prevent outbound requests to internal ranges, regardless of what the application code does. In cloud environments this is typically achieved via:

  • Azure: Virtual Network Service Endpoints + Network Security Groups blocking outbound traffic to private CIDR ranges; Azure Firewall with explicit outbound allowlist rules
  • AWS: Security Groups and NACLs blocking outbound to RFC 1918 ranges; Instance Metadata Service v2 (IMDSv2) requiring session-oriented requests that are harder to exploit via SSRF
  • GCP: VPC firewall rules blocking outbound; Workload Identity eliminating the need for exposed metadata credentials entirely

Relying on application code alone is a single-point-of-failure design. Relying on network controls alone leaves you vulnerable if the application is deployed outside the expected network. The two layers together are robust.

Also consider whether your API needs to make outbound calls at all. If the answer is yes but only to a fixed set of known domains, those domains should be enumerated in configuration, not supplied by end users. Eliminating the user-supplied URL surface entirely is the strongest SSRF defence available.

Defence-in-Depth Checklist

Use this as a pre-deployment review for any ASP.NET Core endpoint that makes user-influenced outbound HTTP calls:

  • Scheme enforcement โ€” reject non-HTTPS schemes before DNS resolution
  • URI parsing โ€” use Uri.TryCreate with UriKind.Absolute; reject relative URIs and malformed inputs
  • Hostname allowlist โ€” if the set of valid destinations is known, enumerate them
  • DNS resolution validation โ€” resolve the hostname and verify each returned IP against private/reserved CIDR ranges
  • DNS rebinding mitigation โ€” make the HTTP connection to the resolved IP, not the hostname, in high-security contexts
  • Redirect following disabled โ€” AllowAutoRedirect = false on HttpClientHandler; validate each redirect URL if following is necessary
  • Request timeouts โ€” set HttpClient.Timeout to a short value to limit SSRF-facilitated data exfiltration via timing
  • Logging โ€” log every rejected outbound request with destination, reason, and caller identity for audit and anomaly detection
  • Network egress rules โ€” enforce at the infrastructure layer, not just application code
  • IMDSv2 (AWS) or equivalent โ€” enable session-oriented metadata endpoints to raise the bar for cloud credential theft

These controls are also directly applicable to the rate limiting and API key patterns covered in the service-to-service authentication guide.

What Does a Vulnerable Pattern Look Like?

Understanding the shape of a vulnerable endpoint is as important as knowing the defences. At a conceptual level, any controller action or service method that:

  1. Accepts a URL as a string from a request parameter, query string, or JSON body
  2. Constructs a Uri or passes the string directly to HttpClient.GetAsync
  3. Returns or logs any part of the response

...is a candidate for SSRF review. The controller itself does not need to be malicious or carelessly written. SSRF vulnerabilities appear in completely ordinary-looking code: image preview services, RSS aggregators, OAuth callback handlers, payment webhook validators. The vulnerability is architectural, not cosmetic.

Common Anti-Patterns to Avoid

Several approaches that seem reasonable on the surface are ineffective in practice:

Regex-based URL blocking โ€” attackers encode IPs in decimal, octal, hex, or IPv6 forms that most regex patterns do not cover. Regex also cannot account for DNS rebinding.

Checking only the hostname โ€” if you only validate the hostname string without resolving it to an IP, a domain like evil.com that resolves to 192.168.1.1 bypasses the check entirely.

Trusting the X-Forwarded-For or Host header โ€” these headers are trivially spoofed by an attacker and should never be part of your SSRF validation logic.

Blocking specific IPs instead of private ranges โ€” blocking 169.254.169.254 but not fd00:ec2::254 (the IPv6 metadata endpoint for some cloud providers) is a partial block that fails in dual-stack environments.

Relying on timeouts as a primary defence โ€” timeouts limit exfiltration window but do not prevent the SSRF request from reaching the internal destination in the first place.

โ˜• Prefer a one-time tip? Buy us a coffee โ€” every bit helps keep the content coming!

FAQ

What is SSRF in the context of ASP.NET Core APIs?

SSRF (Server-Side Request Forgery) occurs when an attacker tricks your ASP.NET Core API into making an outbound HTTP request to an unintended destination โ€” typically an internal service, cloud metadata endpoint, or protected resource that is not publicly accessible. The attack exploits the trust that internal networks place in requests originating from your server, using your API as an unwitting proxy.

How does SSRF differ from CSRF in ASP.NET Core?

CSRF (Cross-Site Request Forgery) tricks a user's browser into making a request to your application using the user's credentials. SSRF tricks your server into making a request to another service. CSRF is about abusing the user's session; SSRF is about abusing the server's network position. ASP.NET Core's built-in antiforgery tokens protect against CSRF but have no effect on SSRF.

What are the most dangerous SSRF targets in cloud-hosted ASP.NET Core APIs?

The most dangerous targets are cloud Instance Metadata Service (IMDS) endpoints. On AWS, the endpoint 169.254.169.254/latest/meta-data/iam/security-credentials/ exposes IAM role credentials. Azure exposes similar data at 169.254.169.254/metadata/instance. A successful SSRF against these endpoints can give an attacker temporary cloud credentials, potentially enabling them to escalate to full account compromise.

Is it enough to block 127.0.0.1 and 169.254.169.254 in my URL validator?

No โ€” blocklists are never sufficient. Attackers use IP address encoding variations (decimal, octal, hex), IPv6 equivalents, DNS rebinding, redirect chains, and alternative hostnames like localhost to bypass simple blocklists. The correct approach is an allowlist (permit only known-safe destinations) combined with DNS resolution validation that checks resolved IPs against all private and reserved CIDR ranges.

Should I use HttpClientFactory or a raw HttpClient for SSRF-sensitive calls?

Always use IHttpClientFactory with a named client configured with a custom HttpClientHandler. This ensures AllowAutoRedirect = false is applied consistently, gives you a single place to apply timeout settings, and plays well with .NET's connection pooling. A raw new HttpClient() instantiated in a controller method bypasses the factory's lifecycle management and makes it harder to enforce consistent security configuration.

How do I test my ASP.NET Core API for SSRF vulnerabilities?

Static analysis tools like Semgrep (with SSRF rules), Snyk, and the Roslyn security analyzers can flag patterns where user input reaches HttpClient. For dynamic testing, use a tool like OWASP ZAP or Burp Suite with SSRF payloads. In your own integration tests, mock an internal endpoint and verify that your validator correctly blocks requests targeting private IP ranges.

Does .NET 10 add any built-in SSRF protection?

As of .NET 10, there is no built-in SSRF validation in HttpClient or HttpClientFactory. Microsoft recommends following the OWASP SSRF Prevention Cheat Sheet and implementing application-level validation as described in this article. The IHttpClientFactory infrastructure makes it straightforward to centralise the configuration, but the validation logic remains the application's responsibility.