Make ASP.NET handle reverse proxies

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
2
down vote

favorite












In my ASP.NET MVC application there are occasions where I need to generate an absolute URI, such as when sending an email confirmation link to a user. The most straightforward approach is to use UrlHelper.Action() with the protocol parameter, and this has worked well for me.



However now my server is behind a reverse proxy. So the absolute URIs that I'm generating are pointing to backendserver.myorg.local instead of publicsite.example.com, and that's not good for business.



So I wrote this method to inspect the Origin header. Do you see any security problems or other reasons this might not be a good solution?



static class UrlHelperExtensions

private const string OriginHeader = "Origin";

/// <summary>
/// Generates a full absolute URI, with HTTP(S), host name, and everything. Is reverse-proxy-aware,
/// so will generate public URIs in the event that our server is behind a reverse proxy.
/// </summary>
/// <param name="urlHelper"></param>
/// <param name="actionName"></param>
/// <param name="controllerName"></param>
/// <param name="routeValues"></param>
/// <returns></returns>
public static string AbsoluteAction(this UrlHelper urlHelper, string actionName, string controllerName, object routeValues = null)

if (urlHelper == null)
throw new ArgumentNullException(nameof(urlHelper), $"nameof(urlHelper) is null.");

var protocol = urlHelper.RequestContext.HttpContext.Request.Url.Scheme; // Should be "http" or "https"
var localUriString = urlHelper.Action(actionName, controllerName, routeValues, protocol);

var originValues = urlHelper.RequestContext.HttpContext.Request.Headers.GetValues(OriginHeader)
.Where(x => !String.IsNullOrWhiteSpace(x))
.ToList();

if (!originValues.Any())
return localUriString; // We are NOT behind a reverse proxy. Easy peasy, return local URI.

var originUriString = originValues.First(); // There should only be only 1 Origin header, but theoretically it's possible to have more than 1.
var originUri = parseOriginUri(originUriString);

var uriBuilder = new UriBuilder(localUriString);
uriBuilder.Scheme = originUri.Scheme; // AKA "protocol" (http or https)
uriBuilder.Host = originUri.Host;
uriBuilder.Port = originUri.Port;

return uriBuilder.ToString();


private static Uri parseOriginUri(string originText)

try

return new Uri(originText);

catch (UriFormatException ex)

throw new UriFormatException($"OriginHeader header URI is in an invalid format: "originText"", ex);





Note: Right now I don't have any requirements to actually manipulate the URL path.







share|improve this question

























    up vote
    2
    down vote

    favorite












    In my ASP.NET MVC application there are occasions where I need to generate an absolute URI, such as when sending an email confirmation link to a user. The most straightforward approach is to use UrlHelper.Action() with the protocol parameter, and this has worked well for me.



    However now my server is behind a reverse proxy. So the absolute URIs that I'm generating are pointing to backendserver.myorg.local instead of publicsite.example.com, and that's not good for business.



    So I wrote this method to inspect the Origin header. Do you see any security problems or other reasons this might not be a good solution?



    static class UrlHelperExtensions

    private const string OriginHeader = "Origin";

    /// <summary>
    /// Generates a full absolute URI, with HTTP(S), host name, and everything. Is reverse-proxy-aware,
    /// so will generate public URIs in the event that our server is behind a reverse proxy.
    /// </summary>
    /// <param name="urlHelper"></param>
    /// <param name="actionName"></param>
    /// <param name="controllerName"></param>
    /// <param name="routeValues"></param>
    /// <returns></returns>
    public static string AbsoluteAction(this UrlHelper urlHelper, string actionName, string controllerName, object routeValues = null)

    if (urlHelper == null)
    throw new ArgumentNullException(nameof(urlHelper), $"nameof(urlHelper) is null.");

    var protocol = urlHelper.RequestContext.HttpContext.Request.Url.Scheme; // Should be "http" or "https"
    var localUriString = urlHelper.Action(actionName, controllerName, routeValues, protocol);

    var originValues = urlHelper.RequestContext.HttpContext.Request.Headers.GetValues(OriginHeader)
    .Where(x => !String.IsNullOrWhiteSpace(x))
    .ToList();

    if (!originValues.Any())
    return localUriString; // We are NOT behind a reverse proxy. Easy peasy, return local URI.

    var originUriString = originValues.First(); // There should only be only 1 Origin header, but theoretically it's possible to have more than 1.
    var originUri = parseOriginUri(originUriString);

    var uriBuilder = new UriBuilder(localUriString);
    uriBuilder.Scheme = originUri.Scheme; // AKA "protocol" (http or https)
    uriBuilder.Host = originUri.Host;
    uriBuilder.Port = originUri.Port;

    return uriBuilder.ToString();


    private static Uri parseOriginUri(string originText)

    try

    return new Uri(originText);

    catch (UriFormatException ex)

    throw new UriFormatException($"OriginHeader header URI is in an invalid format: "originText"", ex);





    Note: Right now I don't have any requirements to actually manipulate the URL path.







    share|improve this question





















      up vote
      2
      down vote

      favorite









      up vote
      2
      down vote

      favorite











      In my ASP.NET MVC application there are occasions where I need to generate an absolute URI, such as when sending an email confirmation link to a user. The most straightforward approach is to use UrlHelper.Action() with the protocol parameter, and this has worked well for me.



      However now my server is behind a reverse proxy. So the absolute URIs that I'm generating are pointing to backendserver.myorg.local instead of publicsite.example.com, and that's not good for business.



      So I wrote this method to inspect the Origin header. Do you see any security problems or other reasons this might not be a good solution?



      static class UrlHelperExtensions

      private const string OriginHeader = "Origin";

      /// <summary>
      /// Generates a full absolute URI, with HTTP(S), host name, and everything. Is reverse-proxy-aware,
      /// so will generate public URIs in the event that our server is behind a reverse proxy.
      /// </summary>
      /// <param name="urlHelper"></param>
      /// <param name="actionName"></param>
      /// <param name="controllerName"></param>
      /// <param name="routeValues"></param>
      /// <returns></returns>
      public static string AbsoluteAction(this UrlHelper urlHelper, string actionName, string controllerName, object routeValues = null)

      if (urlHelper == null)
      throw new ArgumentNullException(nameof(urlHelper), $"nameof(urlHelper) is null.");

      var protocol = urlHelper.RequestContext.HttpContext.Request.Url.Scheme; // Should be "http" or "https"
      var localUriString = urlHelper.Action(actionName, controllerName, routeValues, protocol);

      var originValues = urlHelper.RequestContext.HttpContext.Request.Headers.GetValues(OriginHeader)
      .Where(x => !String.IsNullOrWhiteSpace(x))
      .ToList();

      if (!originValues.Any())
      return localUriString; // We are NOT behind a reverse proxy. Easy peasy, return local URI.

      var originUriString = originValues.First(); // There should only be only 1 Origin header, but theoretically it's possible to have more than 1.
      var originUri = parseOriginUri(originUriString);

      var uriBuilder = new UriBuilder(localUriString);
      uriBuilder.Scheme = originUri.Scheme; // AKA "protocol" (http or https)
      uriBuilder.Host = originUri.Host;
      uriBuilder.Port = originUri.Port;

      return uriBuilder.ToString();


      private static Uri parseOriginUri(string originText)

      try

      return new Uri(originText);

      catch (UriFormatException ex)

      throw new UriFormatException($"OriginHeader header URI is in an invalid format: "originText"", ex);





      Note: Right now I don't have any requirements to actually manipulate the URL path.







      share|improve this question











      In my ASP.NET MVC application there are occasions where I need to generate an absolute URI, such as when sending an email confirmation link to a user. The most straightforward approach is to use UrlHelper.Action() with the protocol parameter, and this has worked well for me.



      However now my server is behind a reverse proxy. So the absolute URIs that I'm generating are pointing to backendserver.myorg.local instead of publicsite.example.com, and that's not good for business.



      So I wrote this method to inspect the Origin header. Do you see any security problems or other reasons this might not be a good solution?



      static class UrlHelperExtensions

      private const string OriginHeader = "Origin";

      /// <summary>
      /// Generates a full absolute URI, with HTTP(S), host name, and everything. Is reverse-proxy-aware,
      /// so will generate public URIs in the event that our server is behind a reverse proxy.
      /// </summary>
      /// <param name="urlHelper"></param>
      /// <param name="actionName"></param>
      /// <param name="controllerName"></param>
      /// <param name="routeValues"></param>
      /// <returns></returns>
      public static string AbsoluteAction(this UrlHelper urlHelper, string actionName, string controllerName, object routeValues = null)

      if (urlHelper == null)
      throw new ArgumentNullException(nameof(urlHelper), $"nameof(urlHelper) is null.");

      var protocol = urlHelper.RequestContext.HttpContext.Request.Url.Scheme; // Should be "http" or "https"
      var localUriString = urlHelper.Action(actionName, controllerName, routeValues, protocol);

      var originValues = urlHelper.RequestContext.HttpContext.Request.Headers.GetValues(OriginHeader)
      .Where(x => !String.IsNullOrWhiteSpace(x))
      .ToList();

      if (!originValues.Any())
      return localUriString; // We are NOT behind a reverse proxy. Easy peasy, return local URI.

      var originUriString = originValues.First(); // There should only be only 1 Origin header, but theoretically it's possible to have more than 1.
      var originUri = parseOriginUri(originUriString);

      var uriBuilder = new UriBuilder(localUriString);
      uriBuilder.Scheme = originUri.Scheme; // AKA "protocol" (http or https)
      uriBuilder.Host = originUri.Host;
      uriBuilder.Port = originUri.Port;

      return uriBuilder.ToString();


      private static Uri parseOriginUri(string originText)

      try

      return new Uri(originText);

      catch (UriFormatException ex)

      throw new UriFormatException($"OriginHeader header URI is in an invalid format: "originText"", ex);





      Note: Right now I don't have any requirements to actually manipulate the URL path.









      share|improve this question










      share|improve this question




      share|improve this question









      asked Aug 1 at 12:10









      Phil

      1334




      1334




















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          1
          down vote













          This is based on a misunderstanding of the Origin header. Chrome and Safari add this header to their web requests, but other browsers don't necessarily. You only have to test this from a browser like Internet Explorer or Firefox to find out it won't work.



          The Forwarded header is probably more what you're looking for.



          This header-based approach could be a security issue as well, depending on your environment. If your server is accessed directly somehow, then you're basically giving the remote client control over what URLs will be generated. All the client would need to do is manipulate the right headers in the right way. Giving remote clients control over your internal URLs sounds like a very bad thing.



          You're probably better off using a server-side setting (perhaps in Web.config) that contains the public URI, and using that server-side setting instead.






          share|improve this answer





















            Your Answer




            StackExchange.ifUsing("editor", function ()
            return StackExchange.using("mathjaxEditing", function ()
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            );
            );
            , "mathjax-editing");

            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "196"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            convertImagesToLinks: false,
            noModals: false,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );








             

            draft saved


            draft discarded


















            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f200742%2fmake-asp-net-handle-reverse-proxies%23new-answer', 'question_page');

            );

            Post as a guest






























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            1
            down vote













            This is based on a misunderstanding of the Origin header. Chrome and Safari add this header to their web requests, but other browsers don't necessarily. You only have to test this from a browser like Internet Explorer or Firefox to find out it won't work.



            The Forwarded header is probably more what you're looking for.



            This header-based approach could be a security issue as well, depending on your environment. If your server is accessed directly somehow, then you're basically giving the remote client control over what URLs will be generated. All the client would need to do is manipulate the right headers in the right way. Giving remote clients control over your internal URLs sounds like a very bad thing.



            You're probably better off using a server-side setting (perhaps in Web.config) that contains the public URI, and using that server-side setting instead.






            share|improve this answer

























              up vote
              1
              down vote













              This is based on a misunderstanding of the Origin header. Chrome and Safari add this header to their web requests, but other browsers don't necessarily. You only have to test this from a browser like Internet Explorer or Firefox to find out it won't work.



              The Forwarded header is probably more what you're looking for.



              This header-based approach could be a security issue as well, depending on your environment. If your server is accessed directly somehow, then you're basically giving the remote client control over what URLs will be generated. All the client would need to do is manipulate the right headers in the right way. Giving remote clients control over your internal URLs sounds like a very bad thing.



              You're probably better off using a server-side setting (perhaps in Web.config) that contains the public URI, and using that server-side setting instead.






              share|improve this answer























                up vote
                1
                down vote










                up vote
                1
                down vote









                This is based on a misunderstanding of the Origin header. Chrome and Safari add this header to their web requests, but other browsers don't necessarily. You only have to test this from a browser like Internet Explorer or Firefox to find out it won't work.



                The Forwarded header is probably more what you're looking for.



                This header-based approach could be a security issue as well, depending on your environment. If your server is accessed directly somehow, then you're basically giving the remote client control over what URLs will be generated. All the client would need to do is manipulate the right headers in the right way. Giving remote clients control over your internal URLs sounds like a very bad thing.



                You're probably better off using a server-side setting (perhaps in Web.config) that contains the public URI, and using that server-side setting instead.






                share|improve this answer













                This is based on a misunderstanding of the Origin header. Chrome and Safari add this header to their web requests, but other browsers don't necessarily. You only have to test this from a browser like Internet Explorer or Firefox to find out it won't work.



                The Forwarded header is probably more what you're looking for.



                This header-based approach could be a security issue as well, depending on your environment. If your server is accessed directly somehow, then you're basically giving the remote client control over what URLs will be generated. All the client would need to do is manipulate the right headers in the right way. Giving remote clients control over your internal URLs sounds like a very bad thing.



                You're probably better off using a server-side setting (perhaps in Web.config) that contains the public URI, and using that server-side setting instead.







                share|improve this answer













                share|improve this answer



                share|improve this answer











                answered Aug 1 at 15:48









                Phil

                1334




                1334






















                     

                    draft saved


                    draft discarded


























                     


                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function ()
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f200742%2fmake-asp-net-handle-reverse-proxies%23new-answer', 'question_page');

                    );

                    Post as a guest













































































                    Popular posts from this blog

                    Greedy Best First Search implementation in Rust

                    Function to Return a JSON Like Objects Using VBA Collections and Arrays

                    C++11 CLH Lock Implementation