ASP.NET - Informações do endereço IP e da localização


 Neste artigo veremos como retornar o endereço IP do cliente e a informação de localização na ASP.NET Core.

Na ASP.NET Core, você pode obter facilmente o endereço IP do cliente usando o objeto HttpContext no Controller, simplificando o acesso a essas informações importantes durante uma requisição.

Exemplo:

public class MeuController : ControllerBase
{
   [HttpGet]
   public ActionResult Get()
   {
        var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString();
       // ...
   }
}
Essa abordagem funciona bem quando nossa aplicação está diretamente exposta à internet, operando sem a camada intermediária de um proxy reverso.

No entanto, é comum que a maioria das aplicações web usar camadas intermediárias, como proxies reversos ou balanceadores de carga em sua arquitetura e neste caso o método de recuperação direta pode gerar o IP do proxy em vez do IP do cliente.

Para obter o endereço IP real do cliente, podemos extraí-lo do valor do cabeçalho ‘X-Forwarded-For’. Para fazer isso, primeiro temos que configurar ForwardHeaderOptions no Program.cs:
 

builder.Services.Configure<ForwardedHeadersOptions>(options => {
      options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
      options.KnownNetworks.Clear();
      options.KnownProxies.Clear();
});

A seguir no controlador, podemos retornar o endereço IP atual do cliente da seguinte forma:

public class MeuController : ControllerBase
{
   [HttpGet]
   public ActionResult Get()
   {
     var ipAddress = HttpContext.GetServerVariable("HTTP_X_FORWARDED_FOR");
     // ...
   }
}

Se usamos o serviço de uma CloudFare então podemos acessar o endereço IP real do cliente a partir do cabeçalho 'CF-CONNECTING-IP' fazendo assim:

public class MeuController : ControllerBase
{
   [HttpGet]
   public ActionResult Get()
   {
      var ipAddress = Request.Headers["CF-CONNECTING-IP"];
    // ...
   }
}

Para obter informações de localização com base no endereço IP do cliente, utilizaremos os serviços fornecidos por ip-api.com.

Primeiro, criamos a classe IpApiClient:

public class IpApiClient(HttpClient httpClient)
{
  private const string BASE_URL = "http://ip-api.com";
  private readonly HttpClient _httpClient = httpClient;

  public async Task<IpApiResponse?> Get(string? ipAddress, CancellationToken ct)
  {
     var route = $"{BASE_URL}/json/{ipAddress}";
     var response = await _httpClient.GetFromJsonAsync<IpApiResponse>(route, ct);
     return response;
  }
}

A seguir definimos a classe IpApiResonse:

public sealed class IpApiResponse
{
   public string? status { get; set; }
   public string? continent { get; set; }
   public string? country { get; set; }
   public string? regionName { get; set; }
   public string? city { get; set; }
   public string? district { get; set; }
   public string? zip { get; set; }
   public double? lat { get; set; }
   public double? lon { get; set; }
   public string? isp { get; set; }
   public string? query { get; set; }
}

Precisamos adicionar e registrar o HttpClient para IpApiClient dentro do container de serviço, o que pode ser feito no arquivo Program.cs:

builder.Services.AddHttpClient<IpApiClient>();

Agora podemos recuperar as informações de localização IP do cliente:

[HttpGet]
public async Task<ActionResult> Get(CancellationToken ct)
{
   try
   {
     var ipAddress = HttpContext.GetServerVariable("HTTP_X_FORWARDED_FOR") ??  
                     HttpContext.Connection.RemoteIpAddress?.ToString();

     var ipAddressWithoutPort = ipAddress?.Split(':')[0];

     var ipApiResponse = await _ipApiClient.Get(ipAddressWithoutPort, ct);

     var response = new
     {
        IpAddress = ipAddressWithoutPort,
        Country = ipApiResponse?.country,
        Region = ipApiResponse?.regionName,
        City = ipApiResponse?.city,
        District = ipApiResponse?.district,
        PostCode = ipApiResponse?.zip,
        Longitude = ipApiResponse?.lon.GetValueOrDefault(),
        Latitude = ipApiResponse?.lat.GetValueOrDefault(),
      };
       return Ok(response);
    }
    catch (Exception ex)
    {
      return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
    }
}

Às vezes, o IP do cliente inclui um número de porta e assim podemos remover o número da porta usando o seguinte código:

var ipAddressWithoutPort = ipAddress?.Split(':')[0];

Esse código define um endpoint HTTP GET em um controller ASP.NET Core que usa cliente chamado IpApiClient para consultar informações de localização geográfica do endereço IP do cliente.

Ele obtém o endereço IP real do cliente que fez a requisição, tentando primeiro o cabeçalho HTTP X-Forwarded-For (usado por proxies reversos para indicar o IP original do cliente). Se esse cabeçalho não existir, usa o IP da conexão direta (RemoteIpAddress).

Trata casos especiais do IP:

- Se o cabeçalho X-Forwarded-For contiver múltiplos IPs (separados por vírgula), pega apenas o primeiro, que é o IP original.
- Remove a porta do IP caso esteja presente (exemplo: "192.168.0.1:12345" vira "192.168.0.1").
- Consulta informações de localização geográfica desse IP usando um serviço externo (IpApiClient), que provavelmente consome a API do ip-api.com.
- Retorna um JSON com os dados do IP e localização, incluindo país, região, cidade, distrito, código postal, latitude, longitude e ISP (provedor de internet).

Trata erros:
   - Se não conseguir determinar o IP, retorna erro 400 (Bad Request).
   - Se a consulta externa falhar ou retornar status diferente de sucesso, retorna erro 502 (Bad Gateway).
   - Em caso de exceções inesperadas, retorna erro 500 (Internal Server Error) com a mensagem da exceção.
Esse endpoint serve para identificar o IP real do cliente que acessa a aplicação e fornecer informações geográficas detalhadas sobre esse IP, facilitando funcionalidades como geolocalização, auditoria ou personalização de conteúdo.

Considerações finais

Quanto à segurança, ao usar o middleware ForwardedHeaders, configure corretamente os proxies confiáveis para evitar spoofing de IP, e, sempre valide o IP obtido antes de usá-lo.

E estamos conversados...

"Porque do céu se manifesta a ira de Deus sobre toda a impiedade e injustiça dos homens, que detêm a verdade em injustiça."
Romanos 1:18

Referências:


José Carlos Macoratti