Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

Asp.NET Core API İçin Güvenli Client IP Listesi

Merhaba,

Dağıtık platformlar için tercih edilen Asp.NET Core API uygulamalarına herhangi bir kontrol yapılmadığı taktirde herhangi bir IP’den istek gönderilerek client olunabilmektedir. Bizler böyle bir durumda IP seviyesinde kontrol uygulayarak iznimizin dışındaki clientların erişimini engelleyebilir ve böylece uygulamanın client ilişkisini sınırlı tutarak güvenliğini desteklemiş olabiliriz.
Asp.NET Core API İçin Güvenli Client IP Listesi
API uygulamamıza istek yapabilecek olan clientları iki başlık altında tanımlayabiliriz.

  • İzin verilenler(WhiteList)
    API’a sadece erişim izni olanların tanımlandığı listedir. Bu listede olmayan clientlar uygulamaya eriştirilmeyerek, arındırılmaktadır. Tümdengelimsel bir yöntemdir.
  • İzin verilmeyenler(BlackList)
    API’a sadece erişim izni olmayanların tanımlandığı listedir. Bu listede bulunan IP’ler dışındaki tüm clientlar uygulamaya erişebilecektir. Tümevarımsal bir yöntemdir.

API uygulamalarında, IP kontrolü genellikle uygulama seviyesinde olsada bazen controller seviyesinde de gerçekleştirilmesi gerekmektedir. Burada yapısal olarak benimsenecek stratejiyi ihtiyaçlar belirlemektedir lakin teknik olarak uygulama bazlı ya da controller bazlı olması değişkenlik göstermektedir. Eğer ki uygulama bazlı bir IP kontrolü gerçekleştirilecekse bunun için en doğru yöntem bir Middleware yazmak olacaktır. Yok eğer controller bazlı kontrol gerçekleştirilecekse bir ‘ActionFilter’ yazılması gerekecektir. İçeriğimizde her iki yöntemide inceleyeceğiz. Gelin ilk olarak uygulama bazlı IP kontrolünün inşasını ele alarak başlayalım.

Asp.NET Core Uygulama Bazlı IP Kontrol

Öncelik olarak whitelist ya da blacklist’i belirleyerek başlayalım. Bunun için ‘appsettings.json’ dosyasını kullanabiliriz.

{
  "WhiteList": [
    "127.0.0.1",
    "::1"
  ],
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Görüldüğü üzere yukarıdaki kod bloğunda ‘WhiteList’ tanımlanmıştır. ‘Hoca, blacklist nerede?‘ şeklindeki sorunuzu duyar gibiyim… Bir uygulamada sadece birinin tanımlanması yeterli olacaktır. Bu içeriğimizde örnek olarak whitelist üzerinde IP kontrolü yapacağız. Blacklist buradakinin tam tersi bir mantıkta kullanılması gerekecektir. Bunu sizlere bırakıyorum…

Şimdi istek yapan client’ın IP değerini tanımladığımız whitelist’te olup olmadığını kontrol eden middleware’i oluşturalım.

    public class IPControlMiddleware
    {
        readonly RequestDelegate _next;
        IConfiguration _configuration;
        public IPControlMiddleware(RequestDelegate next, IConfiguration configuration)
        {
            _configuration = configuration;
            _next = next;
        }
        public async Task Invoke(HttpContext context)
        {
            //Client'ın IP adresini alıyoruz.
            IPAddress remoteIp = context.Connection.RemoteIpAddress;
            //Whitelist'te ki tüm IP'leri çekiyoruz.
            var ips = _configuration.GetSection("WhiteList").AsEnumerable().Where(ip => !string.IsNullOrEmpty(ip.Value)).Select(ip => ip.Value).ToList();

            //Client IP, whitelist'te var mı kontrol ediyoruz.
            if (!ips.Where(ip => IPAddress.Parse(ip).Equals(remoteIp)).Any())
            {
                //Eğer yoksa 403 hatası veriyoruz.
                context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                await context.Response.WriteAsync("Bu IP'nin erişim yetkisi yoktur.");
                return;
            }

            await _next.Invoke(context);
        }
    }

Ardından ilgili middleware’i ‘Startup.cs’ dosyasındaki ‘Configure’ metodunda çağıralım.

    public class Startup
    {
        .
        .
        .
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            .
            .
            .
            app.UseMiddleware<IPControlMiddleware>();
            .
            .
            .
        }
    }

Şimdide oluşturduğumuz yapılanmayı Postman ile test edelim.

Geçerli IP Geçersiz IP
Asp.NET Core API İçin Güvenli Client IP Listesi Asp.NET Core API İçin Güvenli Client IP Listesi

Görüldüğü üzere uygulama bazlı IP kontrolümüzü başarıyla gerçekleştirmiş bulunmaktayız. Şimdi gelin sıradaki controller bazlı IP kontrolünün nasıl yapıldığını inceleyelim.

Asp.NET Core Controller ya da Action Bazlı IP Kontrol

Controller bazlı IP kontrol yapabilmek için gelen istek ile controller ya da action arasına kontrol amaçlı girilmesi gerekmektedir. Bunun için Asp.NET Core’da ‘ActionFilterAttribute’tan türeyen filtreler geliştirilmiştir. Bir request esnasında controllerdan ya da actiondan önce varsa tanımlanmış filtre tetiklenir ve böylece ilgili request’e dair bilgiler ve çalışmalar yapılabilir. Yani uzun sözün kısası, istek yerini bulmadan bizlere işlem zamanı kazandıran yapılar filtrelerdir diyebiliriz.

Şimdi gelin ilk olarak ‘ActionFilterAttribute’ oluşturalım.

    public class IpControlAttribute : ActionFilterAttribute
    {
        IConfiguration _configuration;
        public IpControlAttribute(IConfiguration configuration)
        {
            _configuration = configuration;
        }
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            //Client'ın IP adresini alıyoruz.
            IPAddress remoteIp = context.HttpContext.Connection.RemoteIpAddress;
            //Whitelist'te ki tüm IP'leri çekiyoruz.
            var ips = _configuration.GetSection("WhiteList").AsEnumerable().Where(ip => !string.IsNullOrEmpty(ip.Value)).Select(ip => ip.Value).ToList();

            //Client IP, whitelist'te var mı kontrol ediyoruz.
            if (!ips.Where(ip => IPAddress.Parse(ip).Equals(remoteIp)).Any())
            {
                //Eğer yoksa 403 hatası veriyoruz.
                context.Result = new StatusCodeResult((int)HttpStatusCode.Forbidden);
                return;
            }
            base.OnActionExecuting(context);
        }
    }

Oluşturduğumuz bu filtre ile controller ya da actionları işaretleyerek IP kontrolü yapmak istediğimiz skalayı belirleyebiliriz. Tabi öncelikle bu filtreyi servis olarak uygulamaya eklememiz gerekmektedir.

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            .
            .
            .
            services.AddScoped<IpControlAttribute>();
            .
            .
            .
        }
        .
        .
        .
    }

La hoca! Attributelar direkt olarak kullanılabilir! Ne diye servis olarak ekliyorsun? dediğinizide duyar gibiyim. Evet, normalde attributelar oluşturuldukları yapılar üzerinde direkt kullanılabilir özelliktedirler. Lakin bizim oluşturduğumuz ‘IpControlAttribute’a bakarsanız eğer constructor’dan bir parametre alarak dependency injection ile ICollection nesnesi talep etmektedir. Normalde bu attribute’u çağırdığımızda bu constructor’a bu değeri bizim göndermemiz gerekmektedir. Aksi taktirde;
Asp.NET Core API İçin Güvenli Client IP Listesi
şeklinde bir hata alınacaktır. Dolayısıyla bu değerin DI provider’ından gidebilmesi için stratejik olarak bu attribute’u servis olarak ekleyecek ve ‘ServiceFilter’ olarak kullanacağız.

Velhasıl kullanımına gelirsek eğer;

    [ApiController]
    [Route("api/[controller]")]
    public class ProductsController : ControllerBase
    {
        [HttpGet("[action]")]
        [ServiceFilter(typeof(IpControlAttribute))]
        public IEnumerable<string> GetProducts()
        {
            return new List<string>
            {
                 "pen",
                 "mouse",
                 "keyboard"
            };
        }

        [HttpGet("[action]")]
        public string GetProduct()
        {
            return "pen";
        }
    }

şeklinde action bazlı bir kullanım tercih edebilirsiniz. Burada ‘GetProducts’ IP kontrolüne tabi tutulurken, ‘GetProduct’ ise herhangi bir kontrol olmaksızın tüm IP’ler tarafından tetiklenebilecektir. Eğer ki tüm actionların IP kontrolü yapması icap ederse, ister tüm actionlar [ServiceFilter(typeof(IpControlAttribute))] attribute’u ile işaretlenebileceği gibi ister aşağıdaki gibi sadece controller ilgili attribute ile işaretlenebilir. Burada tercih siz değerli coderlara kalmaktadır 🙂

    [ApiController]
    [Route("api/[controller]")]
    [ServiceFilter(typeof(IpControlAttribute))]
    public class ProductsController : ControllerBase
    {
        [HttpGet("[action]")]
        public IEnumerable<string> GetProducts()
        {
            return new List<string>
            {
                 "pen",
                 "mouse",
                 "keyboard"
            };
        }

        [HttpGet("[action]")]
        public string GetProduct()
        {
            return "pen";
        }
    }

Filtre yapılanmasının da testini gerçekleştirirsek eğer;

Geçerli IP Geçersiz IP
Asp.NET Core API İçin Güvenli Client IP Listesi Asp.NET Core API İçin Güvenli Client IP Listesi

görüldüğü üzere başarıyla IP kontrolü yapılmaktadır.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Bunlar da hoşunuza gidebilir...

5 Cevaplar

  1. Murad dedi ki:

    Hocam elinize sağlık güzel bir anlatım olmuş. Şöyle bir sorum olacaktı. Kullanıcının ip sini almaya çalıştığımda, server a ait ip tarafıma dönüyor. Json ip dönen api lerden js ile çekiyorum, ancak code behide tarafında ilgili adresi parse ettiğimde yine bana server ip sini dönüyor. Bu konuda nasıl bir yol izlenmelidir?

  2. Gençay dedi ki:

    Şuraya göz atmanızı tavsiye ederim. Bu arada server’da load balancer var mı?

    • murat dedi ki:

      Denedim bunları ancak sonuç alamadım. İlgili host firması ile görüştüm. Client doğrudan server a ulaşamıyormış ara sunucu varmış bunun için remote adresisin üstüne çıkmak lazımmış. http client ip sine ulaşmak lazımmış falan dediler. ancak hiç bir metot bunu sağlamadı

      • Gençay dedi ki:

        Ara sunucu client’ın ip’sini herhangi bir header üzerinden gönderiyor olabilir. Host firmasına bunu da danışmanızı tavsiye ederim.

        • Murat dedi ki:

          Dediğiniz şekilde ilgili sunucu firması ile görüştüm. Bana kodlarımın hatalı olduğunu ve bu kodları kullanmamı söylediler:

          var remote = request.HttpContext.Connection.RemoteIpAddress;
          var local = request.HttpContext.Connection.LocalIpAddress;
          var client = request.HttpContext.Connection.ClientCertificate;
          

          Sorun şu ki ilk ikisi ana server ipsini, diğeri üst ip yi dönüyor ama client hem localde hem hostta null dönüyor. İşin içinden çıkamadım.

Gençay için bir yanıt yazın Yanıtı iptal et

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir