.NET Framework equivalent of IApplicationBuilder.UseForwardedHeaders()
Asked Answered
I

1

5

I'm working with a ASP.NET WebForms application running on .NET Framework 4.7.2 which is sitting behind an Azure Application Gateway. The gateway performs SSL hand-off so is adding a X-Forwarded-Proto="https" header.

I also have a .NET Core API in which I can configure

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseForwardedHeaders();
    app.UseHsts();
    app. //...
}

This is documented here, but is specific to .NET Core.

I've been unable to find an equivalent of UseForwardedHeaders for .NET Framework - is there an equivalent?

Also, the WebForms application is running on OWIN, so I can do something like this, but I'm not sure how to complete it.

public void Configuration(IAppBuilder app)
{
    app.Use(async (context, next) =>
            {
                var forwardedProto = context.Request.Headers["X-Forwarded-Proto"];

                if (forwardedProto.ToLower() == "https")
                {
                    // what now?
                }

                await next();
            });
}

Is there an out-of-the-box solution for .NET Framework, or advice on how best to handle this?

Inosculate answered 26/2, 2021 at 8:52 Comment(3)
That only depends on what you want to do later with this. Take a look here for example ideas. In our systems, we do not rely on the header (as it can be forged) but rather on an additional bool setting that overrides the scheme (so that whenever http comes the app knows that it's https).Innominate
Thanks, but the Application Gateway is configured to redirect all HTTP requests to HTTPS, so no HTTP requests from the client reach the application behind the Application Gateway.Inosculate
I know that, http requests reach your server (as the gateway forwards all incoming https requests as http). Still, the question is, what you expect from the what now? .NET Framework doesn't have Scheme under Request so you can't modify that. Means that you can pretty much leave it as is or rewrite to somewhere else (e.g context.Items) so that it's there if someone needs it down the pipeline.Innominate
I
11

I ended up building my own middleware.

public static class UseForwardedHeadersExtension
{
    private const string ForwardedHeadersAdded = "ForwardedHeadersAdded";

    /// <summary>
    /// Checks for the presence of <c>X-Forwarded-For</c> and <c>X-Forwarded-Proto</c> headers, and if present updates the properties of the request with those headers' details.
    /// </summary>
    /// <remarks>
    /// This extension method is needed for operating our website on an HTTP connection behind a proxy which handles SSL hand-off. Such a proxy adds the <c>X-Forwarded-For</c>
    /// and <c>X-Forwarded-Proto</c> headers to indicate the nature of the client's connection.
    /// </remarks>
    public static IAppBuilder UseForwardedHeaders(this IAppBuilder app)
    {
        if (app == null)
        {
            throw new ArgumentNullException(nameof(app));
        }

        // No need to add more than one instance of this middleware to the pipeline.
        if (!app.Properties.ContainsKey(ForwardedHeadersAdded))
        {
            app.Properties[ForwardedHeadersAdded] = true;

            app.Use(async (context, next) =>
                    {
                        var request = context.Request;

                        if (request.Scheme != Uri.UriSchemeHttps && String.Equals(request.Headers["X-Forwarded-Proto"], Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
                        {
                            var httpContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
                            var serverVars = httpContext.Request.ServerVariables;
                            serverVars["HTTPS"] = "on";
                            serverVars["SERVER_PORT_SECURE"] = "1";
                            serverVars["SERVER_PORT"] = "443";
                            serverVars["HTTP_HOST"] = request.Headers.ContainsKey("X-Forwarded-Host") ? request.Headers["X-Forwarded-Host"] : serverVars["HTTP_HOST"];
                        }

                        await next.Invoke().ConfigureAwait(false);
                    });
        }

        return app;
    }
}
Inosculate answered 2/3, 2021 at 13:35 Comment(5)
This was a good solution. We also added known proxies and known networks options (much like the official ASP.NET Core version) to this so that we could limit the load balancer client to known address ranges.Kunming
Thanks for posting this - it finally solved my problem after days banging my head against a wall.Dendro
Thanks for this, just what I needed. I am using OWIN for OPENID Connect authorization, and the nonce cookie was not being created correctly (the "Secure" attribute was not being set) because the request was not getting to the server as https; as the application gateway was performing https termination. I used the following line to fix my issue... context.Request.Scheme = "https";Giantess
how would i make this work in an old app that still has global.asax and forms authentication? (using auth0 lock.js)Ludendorff
@Ludendorff It's difficult to provide a working answer because my .NET Framework projects have been converted to the OWIN pipeline, but the first place I would go is the Application_BeginRequest method (in global.asax.cs) - in here you can set those HttpContext.Current.Request.ServerVariables["..."] properties.Inosculate

© 2022 - 2024 — McMap. All rights reserved.