Ssrf Server Side in Aspnet with Basic Auth
Ssrf Server Side in Aspnet with Basic Auth
Server-side request forgery (SSRF) in ASP.NET becomes notably nuanced when Basic Authentication is involved. Basic Auth is typically implemented by extracting credentials from the Authorization header (e.g., Authorization: Basic base64(username:password)) and using them to authenticate outbound HTTP calls. This combination can inadvertently expose internal endpoints because the server-side code uses static or easily guessable credentials to call downstream services. An attacker who can influence the target URL may direct the server to internal addresses such as 169.254.169.254 (IMDS), metadata services, or internal management interfaces that are protected only by network-level restrictions. Since Basic Auth credentials are often reused across internal services, reaching an SSRF-prone endpoint with a known or default credential pair can allow the attacker to pivot into otherwise isolated administrative APIs.
In an ASP.NET application, this risk surfaces when an HTTP client is configured to supply Basic Auth headers for every outbound request. For example, if the application builds the Authorization header from a configuration value and does not validate or restrict the destination host, an attacker-supplied URL can route traffic through those credentials to internal resources. The scanner’s BFLA/Privilege Escalation and SSRF checks are designed to detect such patterns by submitting probe URLs (including internal IPs and cloud metadata endpoints) and observing whether the server responds with unauthorized data or errors that reveal internal topology. Because the scan tests the unauthenticated attack surface, it can surface cases where SSRF is reachable without prior login, despite the presence of Basic Auth on outbound calls.
OpenAPI/Swagger spec analysis complements runtime testing by validating whether external definitions and $ref chains inadvertently document internal-only endpoints. If the spec references internal schemas or hosts that should never be exposed, the cross-reference between spec and runtime findings highlights a design gap. When a scanner identifies that an endpoint performs outbound HTTP calls with Basic Auth and accepts arbitrary user-supplied URLs, it flags a high-severity SSRF finding with remediation guidance to enforce strict allowlists for hosts and ports, and to avoid forwarding credentials to user-defined targets.
Basic Auth-Specific Remediation in Aspnet
Remediation focuses on preventing user-controlled URLs from reaching internal resources and ensuring that Basic Auth credentials are not automatically forwarded to arbitrary destinations. The following practices reduce risk in ASP.NET applications.
- Validate and restrict the target host: Maintain an allowlist of permitted hostnames or IP ranges. Reject requests that point to private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), loopback (127.0.0.0/8), and link-local ranges (169.254.0.0/16).
- Do not propagate Basic Auth to user-defined URLs: If the outbound call must use Basic Auth, derive credentials from server-side configuration for known services rather than dynamically attaching them to user-supplied targets.
- Use typed HTTP clients and named handlers: This helps enforce policies per destination and makes it easier to apply different credential strategies for internal versus external services.
Example: Configuring an HttpClient to use Basic Auth only for a pre-approved internal service while rejecting non-allowlisted hosts in ASP.NET.
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public class SecureProxyService
{
private static readonly HttpClient _httpClient = new HttpClient();
private const string AllowedHost = "https://internal-api.example.com";
public async Task GetDataAsync(string userSuppliedPath, CancellationToken ct)
{
// Validate and normalize input
if (string.IsNullOrWhiteSpace(userSuppliedPath))
throw new ArgumentException("Path is required", nameof(userSuppliedPath));
var baseUri = new Uri(AllowedHost);
var requestUri = new Uri(baseUri, userSuppliedPath);
// Ensure the request stays within allowed host
if (!requestUri.Host.Equals(baseUri.Host, StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException("Target host is not allowed");
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
// Apply Basic Auth only for the allowed internal service
var token = Convert.ToBase64String(Encoding.ASCII.GetBytes($"svc_user:SecurePass123"));
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", token);
using var response = await _httpClient.SendAsync(request, ct);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync(ct);
}
}
Example: Rejecting non-allowlisted hosts in minimal API endpoints in ASP.NET Core.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient("internal", client =>
{
client.BaseAddress = new Uri("https://internal-api.example.com");
var token = Convert.ToBase64String(Encoding.ASCII.GetBytes($"svc_user:SecurePass123"));
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", token);
});
var app = builder.Build();
app.MapGet("/fetch/{path}", (IHttpClientFactory factory, string path) =>
{
var allowedHosts = new[] { "internal-api.example.com" };
var baseUri = new Uri("https://internal-api.example.com");
var requestUri = new Uri(baseUri, $"/{path}");
if (!allowedHosts.Contains(requestUri.Host))
return Results.BadRequest("Target host not allowed");
var client = factory.CreateClient("internal");
var response = client.GetAsync(requestUri.PathAndQuery).GetAwaiter().GetResult();
response.EnsureSuccessStatusCode();
return Results.Text(response.Content.ReadAsStringAsync().GetAwaiter().GetResult());
});
app.Run();