Implementação C#: IP Filtering + APIM/API Gateway em Azure
Este guia mostra código completo em C# para implementar IP Filtering robusto no ASP.NET Core, com suporte a CIDR (ranges), leitura de IP real via Forwarded Headers, e integração com listas dinâmicas (ex.: Azure App Configuration / Key Vault).
Nota: não entramos em detalhes do cliente Azure SDK aqui — assume-se que você carregue a lista confiável em
IConfigurationou usando um serviço que você implemente para buscar do App Configuration/Key Vault.
1) Helper: Verificar se um IP pertence a um CIDR (IPv4/IPv6)
using System.Net;
public static class IpUtils
{
public static bool IsInSubnet(IPAddress address, string cidrOrAddress)
{
if (string.IsNullOrWhiteSpace(cidrOrAddress))
return false;
// Single IP (no slash)
if (!cidrOrAddress.Contains("/"))
{
if (IPAddress.TryParse(cidrOrAddress, out var single))
return single.Equals(address);
return false;
}
var parts = cidrOrAddress.Split('/');
if (parts.Length != 2) return false;
if (!IPAddress.TryParse(parts[0], out var baseAddress)) return false;
if (!int.TryParse(parts[1], out var prefixLength)) return false;
var addrBytes = address.GetAddressBytes();
var baseBytes = baseAddress.GetAddressBytes();
// IPv4 vs IPv6 mismatch
if (addrBytes.Length != baseBytes.Length) return false;
var byteCount = addrBytes.Length;
var mask = new byte[byteCount];
var remainingBits = prefixLength;
for (int i = 0; i < byteCount; i++)
{
if (remainingBits >= 8)
{
mask[i] = 0xFF;
remainingBits -= 8;
}
else if (remainingBits > 0)
{
mask[i] = (byte)(~(0xFF >> remainingBits));
remainingBits = 0;
}
else
{
mask[i] = 0x00;
}
}
for (int i = 0; i < byteCount; i++)
{
if ((addrBytes[i] & mask[i]) != (baseBytes[i] & mask[i]))
return false;
}
return true;
}
}
2) Serviço para prover lista dinâmica de IPs/Range (pode buscar de App Configuration / Key Vault)
using Microsoft.Extensions.Options;
public interface IAllowedIpProvider
{
Task<IEnumerable<string>> GetAllowedCidrsAsync(CancellationToken ct = default);
}
public class ConfigurationAllowedIpProvider : IAllowedIpProvider
{
private readonly IConfiguration _config;
private readonly string _configKey = "AllowedIpCidrs"; // ex: comma-separated ou JSON array
public ConfigurationAllowedIpProvider(IConfiguration config)
{
_config = config;
}
public Task<IEnumerable<string>> GetAllowedCidrsAsync(CancellationToken ct = default)
{
var raw = _config[_configKey] ?? "";
// Suporta CSV ou JSON array; aqui simplificamos assumindo CSV
var items = raw.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.Trim())
.ToArray();
return Task.FromResult((IEnumerable<string>)items);
}
}
Em produção, substitua por um provedor que use o Azure App Configuration ou Key Vault. Você pode armazenar um JSON com ranges e usar um refresh token do App Configuration para atualizar dinamicamente.
3) Middleware que verifica IP usando CIDR + Forwarded Headers
using System.Net;
using Microsoft.AspNetCore.Http;
public class IpFilterMiddleware : IMiddleware
{
private readonly IAllowedIpProvider _allowedIpProvider;
public IpFilterMiddleware(IAllowedIpProvider allowedIpProvider)
{
_allowedIpProvider = allowedIpProvider;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
// Obter IP real: priorize X-Forwarded-For (caso APIM / Gateway esteja na frente)
var ipString = GetRemoteIpString(context);
if (!IPAddress.TryParse(ipString, out var remoteIp))
{
// se não conseguir resolver, seja conservador: negar ou permitir? aqui negamos.
context.Response.StatusCode = StatusCodes.Status403Forbidden;
await context.Response.WriteAsync("Forbidden: Unable to determine client IP.");
return;
}
var allowedCidrs = await _allowedIpProvider.GetAllowedCidrsAsync();
var allowed = allowedCidrs.Any(cidr => IpUtils.IsInSubnet(remoteIp, cidr));
if (!allowed)
{
context.Response.StatusCode = StatusCodes.Status403Forbidden;
await context.Response.WriteAsync("Forbidden: Access denied due to IP filtering.");
return;
}
await next(context);
}
private string GetRemoteIpString(HttpContext context)
{
// Se houver X-Forwarded-For, pegue primeiro IP (mais à esquerda é o cliente original)
if (context.Request.Headers.TryGetValue("X-Forwarded-For", out var xff))
{
var first = xff.ToString().Split(',', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault()?.Trim();
if (!string.IsNullOrEmpty(first)) return first;
}
// Fallback para conexão direta
return context.Connection.RemoteIpAddress?.ToString() ?? "";
}
}
4) Extensão para registrar o Middleware
public static class IpFilterExtensions
{
public static IApplicationBuilder UseIpFilterMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<IpFilterMiddleware>();
}
}
5) Registro no Program.cs (exemplo .NET 7+ minimal API)
var builder = WebApplication.CreateBuilder(args);
// Configuração: carregue AllowedIpCidrs via App Configuration ou settings locais
// Exemplo local appsettings.json:
// "AllowedIpCidrs": "203.0.113.0/24,198.51.100.5,10.0.0.0/24"
builder.Services.AddSingleton<IAllowedIpProvider, ConfigurationAllowedIpProvider>();
builder.Services.AddTransient<IpFilterMiddleware>();
var app = builder.Build();
// Muito importante: habilite Forwarded Headers se estiver por trás de Proxy/Gateway
using Microsoft.AspNetCore.HttpOverrides;
var forwardedOptions = new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
};
// Opcional: limite de IPs conhecidos que podem enviar forwarded headers
// forwardedOptions.KnownProxies.Add(IPAddress.Parse("IP_DO_SEU_GATEWAY"));
app.UseForwardedHeaders(forwardedOptions);
// Registre antes de middlewares que protegem endpoints sensíveis
app.UseIpFilterMiddleware();
app.MapGet("/", () => "Hello world");
app.Run();
Importante: se você usa Azure API Management ou Front Door, configure
KnownProxiesouKnownNetworkscom os IPs do gateway para evitar spoofing do cabeçalhoX-Forwarded-For. Para APIM, consulte a documentação para obter ranges que o serviço usa (ou integre via Managed Identity para buscar a lista oficial).
6) Atualização dinâmica (opcional): HostedService que recarrega a lista
Para ambientes onde os ranges mudam, use um IHostedService que busca a lista em intervalos e atualiza um repositório em memória:
using Microsoft.Extensions.Caching.Memory;
public class AllowedIpRefreshService : BackgroundService, IAllowedIpProvider
{
private readonly IConfiguration _configuration;
private readonly IMemoryCache _cache;
private readonly TimeSpan _refreshInterval = TimeSpan.FromMinutes(5);
public AllowedIpRefreshService(IConfiguration configuration, IMemoryCache cache)
{
_configuration = configuration;
_cache = cache;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
var raw = _configuration["AllowedIpCidrs"] ?? "";
var items = raw.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToArray();
_cache.Set("allowedCidrs", items, TimeSpan.FromMinutes(10));
}
catch
{
// log error
}
await Task.Delay(_refreshInterval, stoppingToken);
}
}
public Task<IEnumerable<string>> GetAllowedCidrsAsync(CancellationToken ct = default)
{
_cache.TryGetValue("allowedCidrs", out string[] arr);
return Task.FromResult((IEnumerable<string>)(arr ?? Array.Empty<string>()));
}
}
Registre no Program.cs:
builder.Services.AddMemoryCache();
builder.Services.AddHostedService<AllowedIpRefreshService>();
// como o AllowedIpRefreshService implementa IAllowedIpProvider, registre-o assim:
builder.Services.AddSingleton<IAllowedIpProvider>(sp => sp.GetRequiredService<AllowedIpRefreshService>());
7) Observações de segurança finais
- Evite spoofing: configure
ForwardedHeadersOptions.KnownProxies/KnownNetworkspara que apenas proxies confiáveis possam enviarX-Forwarded-For. - Teste em staging: valide ranges e CIDRs antes de aplicar em produção — um erro pode bloquear tráfego legítimo.
- Logs e métricas: envie eventos de requisições bloqueadas para Azure Monitor / App Insights.
- Integre com APIM: use políticas de APIM como primeira linha e deixe o middleware como reforço no backend.
Conclusão
Este MDX fornece a implementação prática em C# para IP Filtering com suporte a ranges e atualizações dinâmicas. Combine com APIM/WAF para máxima proteção em ambientes Azure.
