7
\$\begingroup\$

Description

Aplication type: WEB API

.NET Framework: 4.5

MVC: 5

I need caller host ip in my custom action filter and then in action. On some enviroments we have configured load balancer which stores caller ip in header "HTTP_X_FORWARDED_FOR". I nedd my code works with LB or without.

CODE

Filter

using System.Collections.Generic;
using System.Web;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;

namespace FAPTest.TestApp.ActionFilters
{
    public class AccessControlFilter : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            string ip = HttpContext.Current.Request.UserHostAddress;

            string sourceIp = HttpContext.Current.Request.ServerVariables.Get("HTTP_X_FORWARDED_FOR");

            string ipAddress = ip;

            if (!string.IsNullOrEmpty(sourceIp))
            {
                ipAddress = sourceIp;
            }

            actionContext.Request.Properties.Add(new KeyValuePair<string, object>("ipAddress", ipAddress));                 
        }        
    }
}

Controller

using System.Web.Http;

namespace FAPTest.TestApp.Controllers
{
    public class TestController : ApiController
    {
        [HttpGet]
        public string Test()
        {
            object o = null;
            string ipAddress = null;

            if (Request.Properties.TryGetValue("ipAddress", out o))
            {
                ipAddress = (string)o;
            }

            return ipAddress;
        }
    }
}

Filter registration:

using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using FAPTest.TestApp.ActionFilters;

namespace FAPTest.TestApp
{
    public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            GlobalConfiguration.Configuration.Filters.Add(new AccessControlFilter());  
        }
    }
}

Questions

Is this code safe?

What can I improve?

Are there some pitfals?

\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

Here is a pattern I've used successfully. However, you need to be aware that the X-FORWARDED-FOR header may contain a comma separated list of ip addresses. This solution also helps with protocol sniffing, because most secure sites will need to force the web browser to SSL, and if you are using SSL OFFLOADING, you'll need to use the x-forwarded-proto header to determine if a redirect is required.

        string ipAddress = string.Empty;
        string protocol = string.Empty;
        string port = string.Empty;

        if (Request.RequestContext.HttpContext.Request.Headers.GetValues("X-Forwarded-For") != null)
        {
            protocol = (Request.RequestContext.HttpContext.Request.Headers.GetValues("X-Forwarded-Proto")).FirstOrDefault();
            ipAddress = (Request.RequestContext.HttpContext.Request.Headers.GetValues("X-Forwarded-For")).FirstOrDefault();
            port = Request.RequestContext.HttpContext.Request.Headers.GetValues("X-Forwarded-Port").FirstOrDefault();
        }

        if (string.IsNullOrWhiteSpace(protocol))
        {
            protocol = (this.Request.IsSecureConnection ? "HTTPS" : "HTTP");
            ipAddress = this.Request.UserHostAddress;
        }

Example format of header value:

X-Forwarded-For: client, proxy1, proxy2

See https://en.wikipedia.org/wiki/X-Forwarded-For

\$\endgroup\$
0
0
\$\begingroup\$

Improvements which I made:

Ugly code:

object o = null;
string ipAddress = null;

if (Request.Properties.TryGetValue("ipAddress", out o))
{
    ipAddress = (string)o;
}

I refactored to nicer one:

string ipAddress = (string)(Request.Properties.Keys.Contains("ipAddress") ? Request.Properties["ipAddress"] : null);
\$\endgroup\$
1
  • \$\begingroup\$ well, I'm not sure if this is nicer. I prefer the ugly version or you can write your own extension like var result = Request.Properties.GetValueOrDefault("ipAddress") \$\endgroup\$
    – t3chb0t
    Commented Jul 28, 2016 at 15:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.