Ssrf in Aspnet with Mutual Tls
Ssrf in Aspnet with Mutual Tls — how this specific combination creates or exposes the vulnerability
Server-Side Request Forgery (SSRF) in an ASP.NET application becomes more nuanced when Mutual TLS (mTLS) is enforced. With mTLS, the server presents a certificate and also requires the client to present a valid certificate. This setup is typically used to authenticate and authorize services or partners that call the API. SSRF occurs when an attacker can coerce the server into making arbitrary outbound requests to internal or external endpoints.
In an ASP.NET context, SSRF often arises through user-supplied URLs used with HttpClient, HttpWebRequest, or third-party HTTP clients. Even when mTLS is in place for inbound calls, the server’s outbound HTTP client may not enforce the same strict certificate validation or may be configured to use a default handler that does not verify the server identity of the target endpoint. This can allow an attacker to supply an internal address (e.g., http://169.254.169.254/latest/meta-data/ on cloud instances) or a sensitive internal service that the server-side mTLS client trusts implicitly because it does not validate the target’s certificate properly.
The combination of mTLS and SSRF can be dangerous because mTLS ensures strong authentication for incoming requests but may create a false sense of security for outgoing requests. If the ASP.NET application does not validate the server certificate of the outbound target, an attacker can tunnel through the server to reach internal services that are not exposed externally. Additionally, if the server’s outbound client respects proxy configurations or insecure fallback settings, it may inadvertently route requests through internal networks or bypass expected certificate checks.
For example, an attacker might send a crafted request to an endpoint like /api/fetch?url=http://169.254.169.254/latest/meta-data/. Even though the inbound call is protected by mTLS, the server’s outbound HTTP call may not enforce certificate pinning or hostname verification, allowing the request to reach the metadata service. This can lead to exposure of sensitive cloud metadata, internal service URLs, or credentials that are reachable only from within the network.
middleBrick detects SSRF patterns by correlating OpenAPI/Swagger specifications with runtime behavior, including how user input flows into HTTP client calls. It flags endpoints that accept URLs and perform outbound requests without strict certificate validation or network isolation, even when mTLS is used for inbound authentication.
Mutual Tls-Specific Remediation in Aspnet — concrete code fixes
To mitigate SSRF in ASP.NET applications that use Mutual TLS, you must enforce strict certificate validation for all outbound HTTP calls and avoid using user-supplied URLs to construct requests. Below are concrete code examples illustrating secure practices.
1. Configure HttpClient with custom ServerCertificateCustomValidationCallback
Use HttpClientHandler with a callback that validates the server certificate against a known set of policies, including hostname verification.
using System.Net.Http;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
// Replace with specific validation logic
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
{
if (errors == SslPolicyErrors.None)
return true;
// Example: allow only specific thumbprint for internal services
const string allowedThumbprint = "A1B2C3D4E5F6...";
if (cert is X509Certificate2 cert2)
{
return cert2.Thumbprint == allowedThumbprint;
}
return false;
};
using var client = new HttpClient(handler);
var response = await client.GetAsync("https://internal-service.example.com/data");
2. Enforce certificate pinning and hostname validation
Avoid accepting any certificate for internal endpoints. Validate the hostname and certificate chain explicitly.
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
{
if (message.RequestUri.Host.EndsWith("internal.example.com", StringComparison.OrdinalIgnoreCase))
{
// Perform chain validation and check thumbprint or public key
if (errors == SslPolicyErrors.None)
{
// Additional checks: policy chain, revocation, etc.
return true;
}
}
return false;
};
3. Avoid user-supplied URLs for outbound requests; use allowlists
Instead of passing a user-provided URL directly to HttpClient, map it to a predefined set of endpoints.
// Example: map a short identifier to a known base URL
public string MapToKnownEndpoint(string resourceKey)
{
return resourceKey switch
{
"orders" => "https://api.example.com/v1/orders",
"profile" => "https://api.example.com/v1/profile",
_ => throw new ArgumentException("Unknown resource")
};
}
var url = MapToKnownEndpoint(userInput);
var response = await client.GetAsync(url);
4. Use typed clients and configure policies in Startup or Program.cs
Leverage ASP.NET Core’s typed clients to encapsulate secure HTTP calls with predefined handlers.
// Program.cs
builder.Services.AddHttpClient("SecureClient", client =>
{
client.BaseAddress = new Uri("https://internal-service.example.com");
})
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
{
// Enforce mTLS by checking client cert if required and validating server cert
return ValidateServerCertificate(message, cert, chain, errors);
}
});
// Usage
var client = serviceProvider.GetRequiredService<IHttpClientFactory>().CreateClient("SecureClient");
5. Validate input and reject private IP ranges
Ensure that user-supplied URLs do not point to private or local network ranges.
bool IsPrivateUri(Uri uri)
{
if (!uri.IsAbsoluteUri) return true;
var host = uri.Host;
if (IPAddress.TryParse(host, out var address))
{
return IPAddress.IsLoopback(address) ||
address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork &&
(address.GetAddressBytes()[0] == 10 ||
(address.GetAddressBytes()[0] == 172 && address.GetAddressBytes()[1] >= 16 && address.GetAddressBytes()[1] <= 31) ||
(address.GetAddressBytes()[0] == 192 && address.GetAddressBytes()[1] == 168));
}
// Optionally: check known private hostnames
return false;
}
Related CWEs: ssrf
| CWE ID | Name | Severity |
|---|---|---|
| CWE-918 | Server-Side Request Forgery (SSRF) | CRITICAL |
| CWE-441 | Unintended Proxy or Intermediary (Confused Deputy) | HIGH |