Summary
CookieAuthenticationHandler.IsHostRelative and UrlHelperBase.CheckIsLocalUrl are both intended to recognize "host-relative" / "local" URLs, but they disagree on one class of input: paths whose second character is an ASCII control character. The two should agree.
Observation
| Input |
Url.IsLocalUrl (MVC) |
IsHostRelative (Cookie auth) |
/foo |
true |
true |
//foo |
false |
false |
/\foo |
false |
false |
/\tfoo |
false |
true ← inconsistent |
/\r\nfoo |
false |
true ← inconsistent |
/\0foo |
false |
true ← inconsistent |
UrlHelperBase.CheckIsLocalUrl rejects any path containing char.IsControl(...) after the leading / or ~/. IsHostRelative only inspects path[0] and path[1], so a path whose second character is a C0 control byte passes the cookie-auth check while failing the MVC check.
Effect on cookie auth: when a ReturnUrl query value containing a control character is supplied to the configured LoginPath or LogoutPath, cookie auth honors it; in contrast, the equivalent value handed to MVC LocalRedirect, Razor Pages LocalRedirect, or minimal-API Results.LocalRedirect throws InvalidOperationException.
Source pointers
src/Security/Authentication/Cookies/src/CookieAuthenticationHandler.cs — IsHostRelative
src/Mvc/Mvc.Core/src/Routing/UrlHelperBase.cs — CheckIsLocalUrl (reference behavior)
src/Shared/ResultsHelpers/SharedUrlHelper.cs — IsLocalUrl (same reference behavior, used by Results.LocalRedirect)
Expected behavior
IsHostRelative should reject the same inputs that UrlHelperBase.CheckIsLocalUrl rejects (modulo the ~/ prefix, which cookie auth intentionally doesn't accept). Mirroring the HasControlCharacter guard already used by UrlHelperBase is sufficient.
Risk / compat
- No public API change.
- Inputs newly rejected are paths containing C0 control characters in positions 1+. These do not appear in legitimate return URLs (controls are not valid path characters per RFC 3986 and are not produced by
Url.Action / Url.Page / Url.Content).
- Brings cookie auth in line with the rest of the framework's local-URL helpers.
Servicing
Requesting shiproom consideration for backport — small behavioral fix, low compat risk, restores consistency with UrlHelperBase.
Summary
CookieAuthenticationHandler.IsHostRelativeandUrlHelperBase.CheckIsLocalUrlare both intended to recognize "host-relative" / "local" URLs, but they disagree on one class of input: paths whose second character is an ASCII control character. The two should agree.Observation
Url.IsLocalUrl(MVC)IsHostRelative(Cookie auth)/footruetrue//foofalsefalse/\foofalsefalse/\tfoofalsetrue← inconsistent/\r\nfoofalsetrue← inconsistent/\0foofalsetrue← inconsistentUrlHelperBase.CheckIsLocalUrlrejects any path containingchar.IsControl(...)after the leading/or~/.IsHostRelativeonly inspectspath[0]andpath[1], so a path whose second character is a C0 control byte passes the cookie-auth check while failing the MVC check.Effect on cookie auth: when a
ReturnUrlquery value containing a control character is supplied to the configuredLoginPathorLogoutPath, cookie auth honors it; in contrast, the equivalent value handed to MVCLocalRedirect, Razor PagesLocalRedirect, or minimal-APIResults.LocalRedirectthrowsInvalidOperationException.Source pointers
src/Security/Authentication/Cookies/src/CookieAuthenticationHandler.cs—IsHostRelativesrc/Mvc/Mvc.Core/src/Routing/UrlHelperBase.cs—CheckIsLocalUrl(reference behavior)src/Shared/ResultsHelpers/SharedUrlHelper.cs—IsLocalUrl(same reference behavior, used byResults.LocalRedirect)Expected behavior
IsHostRelativeshould reject the same inputs thatUrlHelperBase.CheckIsLocalUrlrejects (modulo the~/prefix, which cookie auth intentionally doesn't accept). Mirroring theHasControlCharacterguard already used byUrlHelperBaseis sufficient.Risk / compat
Url.Action/Url.Page/Url.Content).Servicing
Requesting shiproom consideration for backport — small behavioral fix, low compat risk, restores consistency with
UrlHelperBase.