Open Redirect in Aspnet
How Open Redirect Manifests in ASP.NET
Open Redirect vulnerabilities occur when an application accepts a user-controlled URL and redirects the browser to it without sufficient validation. In ASP.NET, this commonly appears in authentication flows (e.g., after login), logout handlers, or any feature that uses a returnUrl or redirect parameter. Attackers exploit this to craft phishing links that appear legitimate (e.g., https://your-app.com/login?returnUrl=https://evil.com), tricking users into visiting malicious sites after authentication.
ASP.NET-Specific Attack Patterns:
- Unvalidated
returnUrlin Authentication: The default ASP.NET Core template uses?returnUrl=in the login flow. If the application blindly redirects to this parameter after login without validation, it's vulnerable. - Response.Redirect with User Input: Legacy ASP.NET (Web Forms, MVC 5) often uses
Response.Redirect(Request.QueryString["url"])or similar without checks. - URL Helpers with Untrusted Input: Using
Url.ActionorRedirectwith a string built from user input.
Vulnerable Code Example (ASP.NET Core):
// Controller action vulnerable to open redirect
[HttpPost]
public IActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && _authService.Validate(model))
{
// DANGER: No validation of returnUrl
return Redirect(returnUrl ?? "/");
}
return View(model);
}Here, any returnUrl (including https://attacker.com) will be followed post-login. Even if the parameter is optional, an attacker can supply it.
Another Common Pattern (Legacy ASP.NET):
// Web Forms or MVC 5 example
protected void Page_Load(object sender, EventArgs e)
{
string redirect = Request.QueryString["next"];
if (!string.IsNullOrEmpty(redirect))
{
// Direct redirect without validation
Response.Redirect(redirect);
}
}ASP.NET's Url.IsLocalUrl method exists precisely to prevent this, but it's often forgotten or misused (e.g., checking only for null).
ASP.NET-Specific Detection
Manually, search your codebase for:
- Uses of
Response.Redirect,Redirect,RedirectToAction, orLocalRedirectwhere the URL argument comes from query strings, form fields, or headers (Request.QueryString,Request.Form,Request.Headers["Referer"]). - Parameters named
returnUrl,redirect,next,url,callback. - Custom logic that builds redirects using
string.Concator interpolation with user input.
Automated Detection with middleBrick:
middleBrick's scanner automatically probes for open redirects during its 5–15 second black-box scan. It tests endpoints that accept redirect-related parameters by injecting external URLs (e.g., https://example.com) and observing if the application issues a 3xx redirect to that external domain. This is part of its Input Validation and Authentication checks.
To use middleBrick for this:
- Submit your ASP.NET application's login or any endpoint that accepts a
returnUrlparameter to the middleBrick web dashboard. - Or use the CLI:
middlebrick scan https://your-app.com/login - The report will flag any unvalidated redirects under the Input Validation category, with severity based on context (e.g., post-authentication redirects are higher risk). It will show the exact parameter tested and the external URL it attempted.
Because middleBrick scans the live, unauthenticated attack surface, it detects open redirects exactly as an attacker would—by sending payloads and following redirects. It also cross-references any OpenAPI/Swagger spec you provide; if your spec defines a returnUrl parameter but runtime behavior doesn't validate it, that mismatch is highlighted.
ASP.NET-Specific Remediation
Always validate redirect URLs server-side. ASP.NET provides built-in helpers to restrict redirects to local (same-origin) paths. Never trust user input for redirects without validation.
Fix 1: Use Url.IsLocalUrl (ASP.NET Core & MVC 5)
The primary defense is to allow only local URLs (relative paths or same-domain absolute URLs).
// Fixed version with Url.IsLocalUrl
[HttpPost]
public IActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && _authService.Validate(model))
{
// Validate returnUrl is local
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
// Fallback to a safe default
return Redirect("/");
}
}
return View(model);
}Url.IsLocalUrl returns true for:
/Home/Index(relative)~/Home/Index(app-relative)https://yourapp.com/Home(if host matches current request)
It blocks https://evil.com or //evil.com (protocol-relative URLs).
Fix 2: Whitelist Specific Paths (When Needed)
If you must allow redirects to certain external partners (e.g., OAuth callbacks), maintain a whitelist:
private static readonly HashSet<string> _allowedRedirects = new()
{
"https://trusted-partner.com/callback",
"/account/verify"
};
public IActionResult ExternalLogin(string provider, string returnUrl)
{
if (!_allowedRedirects.Contains(returnUrl) && !Url.IsLocalUrl(returnUrl))
{
returnUrl = "/"; // default
}
// ... initiate OAuth challenge with validated returnUrl
}Fix 3: Use LocalRedirect (ASP.NET Core 2.0+)
ASP.NET Core includes LocalRedirect, which throws an exception if the URL isn't local, preventing the redirect entirely.
public IActionResult Logout(string returnUrl)
{
_signInManager.SignOutAsync();
// This will throw if returnUrl is not local
return LocalRedirect(returnUrl ?? "/");
}Additional Hardening:
- Never use
Request.UrlorRequest.Headers["Referer"]for redirects without validation—these can be spoofed. - For APIs that return redirect URLs in JSON (e.g., RESTful auth), validate the same way before issuing a 3xx response.
- Consider encoding the return URL (e.g.,
Url.Encode) when rendering it in views to prevent injection into JavaScript.
After applying fixes, rescan with middleBrick to confirm the issue is resolved. The Input Validation category score should improve, and the specific finding will disappear.