Como posso obter o endereço IP do cliente no asp.net MVC?

311

Sou totalmente novo na pilha do ASP.NET MVC e queria saber o que aconteceu com o simples objeto Page e o objeto Request ServerVariables?

Basicamente, quero extrair o endereço IP do PC cliente, mas não consigo entender como a estrutura atual do MVC mudou tudo isso.

Pelo que entendi, a maioria dos objetos variáveis ​​foi substituída pelas variantes HttpRequest .

Alguém gostaria de compartilhar alguns recursos? Existe realmente um monte de coisas para aprender no mundo do ASP.NET MVC. :)

Por exemplo, eu tenho uma classe estática com esta função atual. Como obtenho o mesmo resultado usando o 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;
}

E como eu chamo essa função na página do controlador?

Melaos
fonte

Respostas:

427

A resposta simples é usar a propriedade HttpRequest.UserHostAddress .

Exemplo: De dentro de um controlador:

using System;
using System.Web.Mvc;

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

            ...
        }
    }
}

Exemplo: De dentro de uma classe auxiliar:

using System.Web;

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

MAS, se a solicitação tiver sido transmitida por um ou mais servidores proxy , o endereço IP retornado pela propriedade HttpRequest.UserHostAddress será o endereço IP do último servidor proxy que retransmitiu a solicitação.

Servidores proxy PODEM usar o padrão de fato de colocar o endereço IP do cliente no cabeçalho X-Forwarded-For HTTP. Além de não haver garantia de que uma solicitação tenha um cabeçalho X-Forwarded-For, também não há garantia de que o X-Forwarded-For não tenha sido SPOOFED .


Resposta original

Request.UserHostAddress

O código acima fornece o endereço IP do cliente sem recorrer a procurar uma coleção. A propriedade Request está disponível em Controllers (ou Views). Portanto, em vez de passar uma classe Page para sua função, você pode passar um objeto Request para obter o mesmo 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
fonte
6
@ makerofthings7: Pode haver vários valores porque vários servidores proxy podem estar encaminhando ao longo da solicitação HTTP do cliente. Se os servidores proxy forem "bem comportados" (em oposição aos proxies intencionalmente anônimos ou apenas aos mal programados), cada um deles aderirá ao IP do anterior no cabeçalho XFF.
Eric J.
14
O que o método isPrivateIP faz?
Eddegroves 9/04
19
":: 1" significa host local. Apenas uma nota simples.
tomg
5
O cabeçalho X-Forwarded-For é adicionado por firewalls e balanceadores de carga que analisam pacotes e agem como um homem no meio. Para preservar o endereço IP do usuário original, esse cabeçalho é adicionado para que as informações originais possam ser recuperadas. Quando o pacote é reescrito, o novo endereço IP geralmente é um IP interno e não é muito útil.
Marko
2
Obrigado, isso me ajudou.
Jack Fairfield
168

Request.ServerVariables["REMOTE_ADDR"] deve funcionar - diretamente em uma exibição ou no corpo do método de ação do controlador (Request é uma propriedade da classe Controller no MVC, não Page).

Está funcionando .. mas você deve publicar em um IIS real, não no virtual.

ovolko
fonte
como eu chamo isso do lado do controlador?
Melaos
lol, ei, isso funciona, o que acontece se eu quiser colocar isso em um objeto de classe como acima? e ainda preciso do objeto de página?
Melaos
11
Eu acho que você poderia usar HttpContext.Current.Request
ovolko
23
A resposta de Adrian (abaixo) é muito melhor - não é necessário fazer uma pesquisa por string mágica. Use Request.UserHostAddress
csauve
Isso sempre retorna o endereço IP do servidor que está executando meu aplicativo. Alguma razão para isso?
21314 Jack Marchetti
101

Muito do código aqui foi muito útil, mas eu o limpei para meus propósitos e adicionei alguns testes. Aqui está o que eu acabei com:

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;
    }
}

E aqui estão alguns testes do NUnit contra esse código (estou usando o Rhino Mocks para zombar do HttpRequestBase, que é a chamada M <HttpRequestBase> abaixo):

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
fonte
2
Isso sempre retorna o endereço IP do servidor que está executando meu aplicativo.
21330 Jack Marchetti
1
Não deve retornar o publicForwardingIps.First()?
21717
1
@Noah, acho que isso não funcionará para endereços IPv6?
precisa saber é
Ótima solução! IPAddress.Parse () também deve ser usado nos outros endereços IP?
Co-der
21

Ocorreu um problema ao usar o descrito acima e precisava do endereço IP de um controlador. Eu usei o seguinte no final:

System.Web.HttpContext.Current.Request.UserHostAddress
Tom
fonte
2
Do controlador de tudo o que tinha a fazer eraHttpContext.Request.UserHostAddress
Serj Sagan
Obrigado. O que eu precisava em uma classe auxiliar, não em um controlador ou em um contexto de exibição. Esta é uma boa resposta universal. 1
Piotr Kula,
@ander O que você quer dizer? Como devo escrever a declaração?
Piotr Kula
1
Na parte superior da classe auxiliar, basta escrever "using System.Web;" e, em seguida, você só precisará escrever "HttpContext.Current.Request.UserHostAddress". Apenas para os programadores preguiçosos, como eu (e explica por que a resposta de Tom e o comentário de Serj)
Ganders
19

Em uma classe, você pode chamar assim:

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; 
}

Eu usei isso em um aplicativo de barbear com ótimos resultados.

Paul Keefe
fonte
Por que você retorna o último endereço de HTTP_X_FORWARDED_FOR? O endereço do cliente não é o primeiro?
Igor Yalovoy
1

Como eu considero meu site protegido por um 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
fonte