¿Cómo puedo obtener la dirección IP del cliente en ASP.NET MVC?

311

Soy totalmente nuevo en la pila ASP.NET MVC, y me preguntaba qué pasó con el simple objeto Page y el objeto Request ServerVariables.

Básicamente, quiero extraer la dirección IP de la PC cliente, pero no entiendo cómo la estructura MVC actual ha cambiado todo esto.

Hasta donde puedo entender, la mayoría de los objetos variables han sido reemplazados por las variantes HttpRequest .

¿Alguien quiere compartir algunos recursos? Realmente hay un mar de cosas para aprender en el mundo ASP.NET MVC. :)

Por ejemplo, tengo una clase estática con esta función actual. ¿Cómo obtengo el mismo resultado con ASP.NET MVC?

public static int getCountry(Page page)
{
    return getCountryFromIP(getIPAddress(page));
}

public static string getIPAddress(Page page)
{
    string szRemoteAddr = page.Request.ServerVariables["REMOTE_ADDR"];
    string szXForwardedFor = page.Request.ServerVariables["X_FORWARDED_FOR"];
    string szIP = "";

    if (szXForwardedFor == null)
    {
        szIP = szRemoteAddr;
    }
    else
    {
        szIP = szXForwardedFor;

        if (szIP.IndexOf(",") > 0)
        {
            string [] arIPs = szIP.Split(',');

            foreach (string item in arIPs)
            {
                if (!isPrivateIP(item))
                {
                    return item;
                }
            }
        }
    }
    return szIP;
}

¿Y cómo llamo a esta función desde la página del controlador?

melaos
fuente

Respuestas:

427

La respuesta simple es usar la propiedad HttpRequest.UserHostAddress .

Ejemplo: desde un controlador:

using System;
using System.Web.Mvc;

namespace Mvc.Controllers
{
    public class HomeController : ClientController
    {
        public ActionResult Index()
        {
            string ip = Request.UserHostAddress;

            ...
        }
    }
}

Ejemplo: desde una clase auxiliar:

using System.Web;

namespace Mvc.Helpers
{
    public static class HelperClass
    {
        public static string GetIPHelper()
        {
            string ip = HttpContext.Current.Request.UserHostAddress;
            ..
        }
    }
}

PERO, si la solicitud ha sido transmitida por uno o más servidores proxy , la dirección IP devuelta por la propiedad HttpRequest.UserHostAddress será la dirección IP del último servidor proxy que transmitió la solicitud.

Los servidores proxy PUEDEN usar el estándar de facto de colocar la dirección IP del cliente en el encabezado HTTP X-Fordered-For . Aparte de que no hay garantía de que una solicitud tenga un encabezado X-Fordered-For, tampoco hay garantía de que el X-Forward-For no haya sido SPOFED .


Respuesta original

Request.UserHostAddress

El código anterior proporciona la dirección IP del Cliente sin tener que buscar una colección. La propiedad Solicitud está disponible en Controladores (o Vistas). Por lo tanto, en lugar de pasar una clase de página a su función, puede pasar un objeto Solicitud para obtener el mismo resultado:

public static string getIPAddress(HttpRequestBase request)
{
    string szRemoteAddr = request.UserHostAddress;
    string szXForwardedFor = request.ServerVariables["X_FORWARDED_FOR"];
    string szIP = "";

    if (szXForwardedFor == null)
    {
        szIP = szRemoteAddr;
    }
    else
    {
        szIP = szXForwardedFor;
        if (szIP.IndexOf(",") > 0)
        {
            string [] arIPs = szIP.Split(',');

            foreach (string item in arIPs)
            {
                if (!isPrivateIP(item))
                {
                    return item;
                }
            }
        }
    }
    return szIP;
}
Adrian Toman
fuente
66
@ makerofthings7: puede haber varios valores porque varios servidores proxy pueden reenviar a lo largo de la solicitud HTTP del cliente. Si los servidores proxy se "comportan bien" (a diferencia de los servidores proxy anónimos intencionados o simplemente los mal programados), cada uno agregará la IP del anterior en el encabezado XFF.
Eric J.
14
¿Qué hace el método isPrivateIP?
eddiegroves
19
":: 1" significa localhost. Solo una simple nota.
tomg
55
El encabezado X-Fordered-For se agrega mediante firewalls y equilibradores de carga que analizan los paquetes y actúan como un hombre en el medio. Para preservar la dirección IP del usuario original, se agrega este encabezado para que se pueda recuperar la información original. Cuando se reescribe el paquete, la nueva dirección IP suele ser una IP interna y no es muy útil.
Marko el
2
Gracias, esto me ayudó.
Jack Fairfield
168

Request.ServerVariables["REMOTE_ADDR"] debería funcionar, ya sea directamente en una vista o en el cuerpo del método de acción del controlador (Request es una propiedad de la clase Controller en MVC, no Page).

Está funcionando ... pero debe publicar en un IIS real, no virtual.

ovolko
fuente
¿Cómo llamo a esto desde el lado del controlador?
melaos
jajaja, eso funciona, ¿qué pasa si deseo poner eso en un objeto de clase como el anterior? ¿y todavía necesito el objeto de página?
melaos
11
Creo que podría usar HttpContext.Current.Request
ovolko
23
La respuesta de Adrián (a continuación) es mucho mejor: no es necesario hacer una búsqueda con una cuerda mágica. Use Request.UserHostAddress
csauve
Esto siempre devuelve la dirección IP del servidor que ejecuta mi aplicación. ¿Alguna razón por qué?
Jack Marchetti
101

Gran parte del código aquí fue muy útil, pero lo limpié para mis propósitos y agregué algunas pruebas. Esto es lo que terminé con:

using System;
using System.Linq;
using System.Net;
using System.Web;

public class RequestHelpers
{
    public static string GetClientIpAddress(HttpRequestBase request)
    {
        try
        {
            var userHostAddress = request.UserHostAddress;

            // Attempt to parse.  If it fails, we catch below and return "0.0.0.0"
            // Could use TryParse instead, but I wanted to catch all exceptions
            IPAddress.Parse(userHostAddress);

            var xForwardedFor = request.ServerVariables["X_FORWARDED_FOR"];

            if (string.IsNullOrEmpty(xForwardedFor))
                return userHostAddress;

            // Get a list of public ip addresses in the X_FORWARDED_FOR variable
            var publicForwardingIps = xForwardedFor.Split(',').Where(ip => !IsPrivateIpAddress(ip)).ToList();

            // If we found any, return the last one, otherwise return the user host address
            return publicForwardingIps.Any() ? publicForwardingIps.Last() : userHostAddress;
        }
        catch (Exception)
        {
            // Always return all zeroes for any failure (my calling code expects it)
            return "0.0.0.0";
        }
    }

    private static bool IsPrivateIpAddress(string ipAddress)
    {
        // http://en.wikipedia.org/wiki/Private_network
        // Private IP Addresses are: 
        //  24-bit block: 10.0.0.0 through 10.255.255.255
        //  20-bit block: 172.16.0.0 through 172.31.255.255
        //  16-bit block: 192.168.0.0 through 192.168.255.255
        //  Link-local addresses: 169.254.0.0 through 169.254.255.255 (http://en.wikipedia.org/wiki/Link-local_address)

        var ip = IPAddress.Parse(ipAddress);
        var octets = ip.GetAddressBytes();

        var is24BitBlock = octets[0] == 10;
        if (is24BitBlock) return true; // Return to prevent further processing

        var is20BitBlock = octets[0] == 172 && octets[1] >= 16 && octets[1] <= 31;
        if (is20BitBlock) return true; // Return to prevent further processing

        var is16BitBlock = octets[0] == 192 && octets[1] == 168;
        if (is16BitBlock) return true; // Return to prevent further processing

        var isLinkLocalAddress = octets[0] == 169 && octets[1] == 254;
        return isLinkLocalAddress;
    }
}

Y aquí hay algunas pruebas de NUnit contra ese código (estoy usando Rhino Mocks para burlarse de HttpRequestBase, que es la llamada M <HttpRequestBase> a continuación):

using System.Web;
using NUnit.Framework;
using Rhino.Mocks;
using Should;

[TestFixture]
public class HelpersTests : TestBase
{
    HttpRequestBase _httpRequest;

    private const string XForwardedFor = "X_FORWARDED_FOR";
    private const string MalformedIpAddress = "MALFORMED";
    private const string DefaultIpAddress = "0.0.0.0";
    private const string GoogleIpAddress = "74.125.224.224";
    private const string MicrosoftIpAddress = "65.55.58.201";
    private const string Private24Bit = "10.0.0.0";
    private const string Private20Bit = "172.16.0.0";
    private const string Private16Bit = "192.168.0.0";
    private const string PrivateLinkLocal = "169.254.0.0";

    [SetUp]
    public void Setup()
    {
        _httpRequest = M<HttpRequestBase>();
    }

    [TearDown]
    public void Teardown()
    {
        _httpRequest = null;
    }

    [Test]
    public void PublicIpAndNullXForwardedFor_Returns_CorrectIp()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(GoogleIpAddress);
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(null);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(GoogleIpAddress);
    }

    [Test]
    public void PublicIpAndEmptyXForwardedFor_Returns_CorrectIp()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(GoogleIpAddress);
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(string.Empty);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(GoogleIpAddress);
    }

    [Test]
    public void MalformedUserHostAddress_Returns_DefaultIpAddress()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(MalformedIpAddress);
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(null);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(DefaultIpAddress);
    }

    [Test]
    public void MalformedXForwardedFor_Returns_DefaultIpAddress()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(GoogleIpAddress);
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(MalformedIpAddress);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(DefaultIpAddress);
    }

    [Test]
    public void SingleValidPublicXForwardedFor_Returns_XForwardedFor()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(GoogleIpAddress);
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(MicrosoftIpAddress);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(MicrosoftIpAddress);
    }

    [Test]
    public void MultipleValidPublicXForwardedFor_Returns_LastXForwardedFor()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(GoogleIpAddress);
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(GoogleIpAddress + "," + MicrosoftIpAddress);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(MicrosoftIpAddress);
    }

    [Test]
    public void SinglePrivateXForwardedFor_Returns_UserHostAddress()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(GoogleIpAddress);
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(Private24Bit);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(GoogleIpAddress);
    }

    [Test]
    public void MultiplePrivateXForwardedFor_Returns_UserHostAddress()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(GoogleIpAddress);
        const string privateIpList = Private24Bit + "," + Private20Bit + "," + Private16Bit + "," + PrivateLinkLocal;
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(privateIpList);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(GoogleIpAddress);
    }

    [Test]
    public void MultiplePublicXForwardedForWithPrivateLast_Returns_LastPublic()
    {
        // Arrange
        _httpRequest.Stub(x => x.UserHostAddress).Return(GoogleIpAddress);
        const string privateIpList = Private24Bit + "," + Private20Bit + "," + MicrosoftIpAddress + "," + PrivateLinkLocal;
        _httpRequest.Stub(x => x.ServerVariables[XForwardedFor]).Return(privateIpList);

        // Act
        var ip = RequestHelpers.GetClientIpAddress(_httpRequest);

        // Assert
        ip.ShouldEqual(MicrosoftIpAddress);
    }
}
Noah Heldman
fuente
2
Esto siempre devuelve la dirección IP del servidor que ejecuta mi aplicación.
Jack Marchetti
1
¿No debería devolver el publicForwardingIps.First()?
andy250
1
@Noah ¿Supongo que esto no funcionará para las direcciones IPv6?
AidanO
Buena solución! ¿Debería IPAddress.Parse () también usarse en las otras direcciones IP?
Co-der
21

Tuve problemas para usar lo anterior y necesitaba la dirección IP de un controlador. Usé lo siguiente al final:

System.Web.HttpContext.Current.Request.UserHostAddress
Tom
fuente
2
Desde el controlador, todo lo que tenía que hacer eraHttpContext.Request.UserHostAddress
Serj Sagan el
Gracias. Eso es lo que necesitaba en una clase auxiliar, no en un controlador o dentro de un contexto de vista. Esta es una buena respuesta universal. +1
Piotr Kula
@gander ¿Qué quieres decir? ¿Cómo debo escribir la declaración?
Piotr Kula
1
En la parte superior de la clase auxiliar, simplemente escriba "using System.Web;", luego solo necesita escribir "HttpContext.Current.Request.UserHostAddress". Solo para los programadores perezosos, como yo (y explica por qué la respuesta de Tom y el comentario de Serj)
reúne el
19

En una clase podrías llamarlo así:

public static string GetIPAddress(HttpRequestBase request) 
{
    string ip;
    try
    {
        ip = request.ServerVariables["HTTP_X_FORWARDED_FOR"];
        if (!string.IsNullOrEmpty(ip))
        {
            if (ip.IndexOf(",") > 0)
            {
                string[] ipRange = ip.Split(',');
                int le = ipRange.Length - 1;
                ip = ipRange[le];
            }
        } else
        {
            ip = request.UserHostAddress;
        }
    } catch { ip = null; }

    return ip; 
}

Utilicé esto en una aplicación de afeitar con excelentes resultados.

Paul Keefe
fuente
¿Por qué devuelve la última dirección de HTTP_X_FORWARDED_FOR? ¿Es la dirección del cliente no la primera?
Igor Yalovoy
1

Cómo considero que mi sitio está detrás de un Amazon AWS Elastic Load Balancer (ELB):

public class GetPublicIp {

    /// <summary>
    /// account for possbility of ELB sheilding the public IP address
    /// </summary>
    /// <returns></returns>
    public static string Execute() {
        try {
            Console.WriteLine(string.Join("|", new List<object> {
                    HttpContext.Current.Request.UserHostAddress,
                    HttpContext.Current.Request.Headers["X-Forwarded-For"],
                    HttpContext.Current.Request.Headers["REMOTE_ADDR"]
                })
            );

            var ip = HttpContext.Current.Request.UserHostAddress;
            if (HttpContext.Current.Request.Headers["X-Forwarded-For"] != null) {
                ip = HttpContext.Current.Request.Headers["X-Forwarded-For"];
                Console.WriteLine(ip + "|X-Forwarded-For");
            }
            else if (HttpContext.Current.Request.Headers["REMOTE_ADDR"] != null) {
                ip = HttpContext.Current.Request.Headers["REMOTE_ADDR"];
                Console.WriteLine(ip + "|REMOTE_ADDR");
            }
            return ip;
        }
        catch (Exception ex) {
            Console.Error.WriteLine(ex.Message);
        }
        return null;
    }
}
sobelito
fuente