5

I am trying to find a way to prevent my aspnetcore application to add "?ReturnUrl=" to the URL. Does anyone know how to do it, using some kind of middleware.

I tried doing it like below but it did not have any effect:

public class RequestHandlerMiddleware
{
    private readonly RequestDelegate _next;

    public RequestHandlerMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        if(context.Request.QueryString.HasValue && context.Request.QueryString.Value.Contains("?ReturnUrl="))
        {
            context.Request.QueryString = new QueryString(string.Empty);
        }
        await _next.Invoke(context);
    }
}

public static class RequestHandlerMiddlewareExtension
{
    public static IApplicationBuilder UseRequestHandlerMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestHandlerMiddleware>();
    }
}

Registration in startup.cs:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/error");
    }

    app.UseDefaultFiles();
    app.UseStaticFiles();

    app.UseAuthentication();
    app.UseRequestHandlerMiddleware();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller}/{action=Index}/{id?}");
    });
}

Lastly, I have also tried some (tweaked) approaches from the older post regarding the same issue for .NET frameworks here (on stackoverflow) but also failed

Edit: I am not using any additional AuthorizationAttribute / Handler other then the 'standard' [Authorize] attribute. Only:

services.AddAuthorization();

Edit 2: I totally forgot that I also register a portion of the startup elsewhere in the application since it is shared:

    public static IServiceCollection Load(IServiceCollection services, IConfiguration config)
    {

        services.AddDbContext<SqlContext>(options =>
        {
            options.UseSqlServer(config.GetConnectionString("DefaultConnection"));
        });

        services.AddIdentity<User, Role>(options =>
        {
            options.Lockout = new LockoutOptions
            {
                AllowedForNewUsers = true,
                DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30),
                MaxFailedAccessAttempts = 5
            };
        })
        .AddEntityFrameworkStores<SqlContext>()
        .AddDefaultTokenProviders()
        .AddUserStore<UserStore<User, Role, SqlContext, Guid>>()
        .AddRoleStore<RoleStore<Role, SqlContext, Guid>>()
        .AddUserManager<ApplicationUserManager>();

        services.Configure<IdentityOptions>(options =>
        {
            options.Password.RequireDigit = false;
            options.Password.RequiredLength = 5;
            options.Password.RequireLowercase = true;
            options.Password.RequireUppercase = false;
            options.Password.RequireNonAlphanumeric = true;

        });

        services.ConfigureApplicationCookie(options =>
        options.Events = new CookieAuthenticationEvents
        {
            OnRedirectToLogin = ctx =>
            {
                if (ctx.Request.Path.StartsWithSegments("/api") &&
                    ctx.Response.StatusCode == (int)HttpStatusCode.OK)
                {
                    ctx.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                }
                else if (ctx.Response.StatusCode == (int)HttpStatusCode.Forbidden)
                {
                    ctx.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                }
                else
                {
                    ctx.Response.Redirect(ctx.RedirectUri);
                }
                return Task.FromResult(0);
            }
        });
        return services;
   }
AmiNadimi
  • 3,488
  • 1
  • 30
  • 46
Bob Meijwaard
  • 337
  • 3
  • 17
  • It sounds like you want to remove ReturnUri in the URLs generated by your program. But you're removing it on the way into the program. If you want to remove them on the way out, you need to look in the response. It might help if you give an example of what you're seeing and what you want it to look like. – Hans Kilian Apr 02 '18 at 10:45
  • Can I ask why you want to prevent it? It serves a propose. – Tratcher Apr 02 '18 at 10:50
  • So when going to http://localhost:3000/ the app will redirect to ...Account/Login?ReturnUrl=%2F, but i just want Account/Login. The reason is that i always redirect to /Index, regardless of at what point the user is redirected away (when unauthorized in most cases). Plus it look cleaner that way. – Bob Meijwaard Apr 02 '18 at 10:53
  • Could you share your authentication handler? – Madison Haynie Apr 02 '18 at 10:57
  • I have added an update: i am not using any (custom) handlers other then [Authorize], which is the standard one. Additionally i have not reqistered an additional middleware handlers. ( services.AddAuthorization();) – Bob Meijwaard Apr 02 '18 at 11:02
  • You're using AddCookies or AddIdentity, they set this parameter, and there's no supported way to turn it off. – Tratcher Apr 02 '18 at 11:40
  • Good point, then i hope to found some unsupported way of achieving that (one that is not to tacky) Also i forgot that i register my identity elsewhere so updated OP. – Bob Meijwaard Apr 02 '18 at 12:12
  • No expert here; maybe you can use ***[This answer](https://stackoverflow.com/a/34221707/1220550)*** or something like it, and set `ReturnUrlParameter` to null or "". Also, see ***[This question](https://stackoverflow.com/q/35342725/1220550)***. – Peter B Apr 03 '18 at 15:58
  • See this discussion, the results of which I dislike but which tell you that the feature isn't there and won't be: https://github.com/aspnet/Security/issues/1682 – philw Jan 15 '19 at 08:55

1 Answers1

2

The first thing that comes to mind is :

[HttpGet]
public IActionResult LogIn()
{
    if (!string.IsNullOrEmpty(Request.QueryString.Value))
        return RedirectToAction("Login");
    return View();
}

Which will remove QueryString part from the URL so that "ReturnUrl" will not stay on user address-bar for long and will reject any QueryString.

Better workaround would be creating your own version of AuthorizeAttribute which will not put a ReturnUrl in QueryString but it seems with new policy based authorization approach coming around, customizing AuthorizeAttribute is discouraged. more

It might be also possible with policy based approach and creating a custom AuthorizationHandler.

(I will post an update as soon as I try it out)

AmiNadimi
  • 3,488
  • 1
  • 30
  • 46