ekrem özer

her yerde olan şeyler.

.Net Core Ip Bazlı Giriş İzni

Merhaba arkadaşlar, bu makalede .net core üzerinde sitenize gelen istekleri Ip adresine göre uygulama, controller ve action seviyesinde engelleme veya izin verme yöntemine değineceğiz. Öncelikle IpPermission isminde bir solition ve IpPermission.Web istaminde bir .net core web uygulaması oluşturuyorum.

Bu projede izin vereceğimiz ya da engelleyeceğimiz Ip adreslerini appsettings.json dosyasında okuyacağım. Bunu için şu json'u ekliyorum;

"IpList": {
    "WhiteList": [ "151.231.234.218", "27.152.82.115", "230.161.224.213" ],
    "BlackList": [ "188.175.76.131", "144.160.233.149", "170.1.75.12" ]
  },

Sonrasında Infrastructure adında bir klasör oluşturarar ilk olarak IpList classımı oluşturuyorum;

namespace IpPermission.Web.Infrastructure
{
    public class IpList
    {
        public List<string> WhiteList { get; set; }
        public List<string> BlackList { get; set; }
    }
}

Bu classımı apsettingsden okuyacağım Ip adreslerini modellemek için kullanacağım, bunun için Startup da ConfigirationServices'e aşağıdaki kodu ekleyip, Json'daki Section ile classımı ilişkilendiriyorum;

public void ConfigureServices(IServiceCollection services)
{
	services.Configure<IpList>(Configuration.GetSection("IpList"));
	services.AddControllersWithViews();
}

Şimdi uygulama bazlı kontrol edebilmek için WhiteIpPermissionMiddleWare ve BlackIpPermissionMiddleWare adında iki tane middleware oluşturacağım, ilk olarak WhiteIpPermissionMiddleWare classımızı inceleyelim;

namespace IpPermission.Web.Infrastructure
{
    public class WhiteIpPermissionMiddleWare
    {
        private readonly RequestDelegate _next;
        private readonly IpList _ipList;

        public WhiteIpPermissionMiddleWare(RequestDelegate next, IOptions<IpList> ipList)
        {
            _next = next;
            _ipList = ipList.Value;
        }

        public async Task Invoke(HttpContext context)
        {
            var currentIp = context.Connection.RemoteIpAddress.ToString();
            var inWhiteList = _ipList.WhiteList.Any(x => x == currentIp);

            if (!inWhiteList)
            {
                context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                return;
            }

            await _next(context);
        }
    }
}

Burada ilk başta oluşturduğumuz IpList classımızı readonly olarak classımınız üstüne property olarak ekiyoruz, sonra constructor metodumuzda parametre olarak IOptions dan türettiğimiz IpList nesnemizi alıyourz, metodun içindede gelen parametrenin value'sunu kullanarak appsettings'deki IpList sectionumuza bu class ile erişebiliyoruz, bu yöntemin sağlıklı çalışması için class adının section adıyla classdaki propertilerin adları ilede sectionun fieldlarının aynı isimde olması gerekiyor.

Sonrasın da middleware'imize gelen requestleri yakalayıp işlem yapmak için RequestDelegate propertisini de ekliyoruz, classımıza Invoke adında Task dönen bir function ekliyoruz ve parametre olarak HttpContext gelmesini bekliyoruz. Metodu inceleyecek olursak;

var currentIp = context.Connection.RemoteIpAddress.ToString();

İstek yapan kullanıcının ip adresini HttpContext nesnesinden bu şekilde yakalayıp stringe pars ediyoruz.

var inWhiteList = _ipList.WhiteList.Any(x => x == currentIp);

_ipList propertysinde gelen Ip adresinin olup olmadığını kontrol etmek için Any metodunu kullanıyoruz.

if (!inWhiteList)
{
	context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
	return;
}

await _next(context);

Eğer Ip adresi WhiteList'de yoksa response'un status kodunu 403 erişim izni yok olarak set ediyoruz ve return ile dönüyoruz. Eğer WhiteList'de Ip adresi var ise bir işlem yapmadan RequestDelegate ile contextimizi dönürüyoruz.

BlackIpPermissionMiddleWare'de ise bunun tam tesini yapıyoruz, Ip adresi BlackList'de varsa 403 dönüyoruz.

namespace IpPermission.Web.Infrastructure
{
    public class BlackIpPermissionMiddleWare
    {
        private readonly RequestDelegate _next;
        private readonly IpList _ipList;

        public BlackIpPermissionMiddleWare(RequestDelegate next, IOptions<IpList> ipList)
        {
            _next = next;
            _ipList = ipList.Value;
        }

        public async Task Invoke(HttpContext context)
        {
            var currentIp = context.Connection.RemoteIpAddress.ToString();
            var inBlackList = _ipList.BlackList.Any(x => x == currentIp);

            if (inBlackList)
            {
                context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                return;
            }

            await _next(context);
        }
    }
}

MiddleWare'lerimizin çalışması için Startup'da Configure metodunda tanımlama yapmamız gerekiyor,

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
	if (env.IsDevelopment())
	{
		app.UseDeveloperExceptionPage();
	}
	else
	{
		app.UseExceptionHandler("/Home/Error");
		// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
		app.UseHsts();
	}
	app.UseHttpsRedirection();
	app.UseStaticFiles();
	app.UseRouting();
	app.UseAuthorization();

	app.UseMiddleware<WhiteIpPermissionMiddleWare>();
	app.UseMiddleware<BlackIpPermissionMiddleWare>();

	app.UseEndpoints(endpoints =>
	{
		endpoints.MapControllerRoute(
			name: "default",
			pattern: "{controller=Home}/{action=Index}/{id?}");
	});
}

18 ve 19. satırlardaki gibi middlewarelerimizi tanımlıyoruz. Bu işlem uygulama seviyinde Ip Adreslerine göre kontol yapmamızı sağlayacaktır. Şimdi de Controller veya Action bazlı nasıl yaparız onu inceleyelim. Bunun için ActionFilterAttribute kullanacağız.

Infrastructure klasörüme WhiteIpPermissionFilter ve BlackIpPermissionFilter adında iki tane class ekliyorum;

namespace IpPermission.Web.Infrastructure
{
    public class BlackIpPermissionFilter : ActionFilterAttribute
    {
        private readonly IpList _ipList;

        public BlackIpPermissionFilter(IOptions<IpList> ipList)
        {
            _ipList = ipList.Value;
        }

        public override void OnActionExecuted(ActionExecutedContext context)
        {
            var currentIp = context.HttpContext.Connection.RemoteIpAddress.ToString();
            var inBlackList = _ipList.BlackList.Any(x => x == currentIp);

            if (inBlackList)
            {
                context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                return;
            }

            base.OnActionExecuted(context);
        }
    }
}
namespace IpPermission.Web.Infrastructure
{
    public class BlackIpPermissionFilter : ActionFilterAttribute
    {
        private readonly IpList _ipList;

        public BlackIpPermissionFilter(IOptions<IpList> ipList)
        {
            _ipList = ipList.Value;
        }

        public override void OnActionExecuted(ActionExecutedContext context)
        {
            var currentIp = context.HttpContext.Connection.RemoteIpAddress.ToString();
            var inBlackList = _ipList.BlackList.Any(x => x == currentIp);

            if (inBlackList)
            {
                context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                return;
            }

            base.OnActionExecuted(context);
        }
    }
}

Filterlarımızı ActionFilterAttribute nesnesinden türetiyoruz ve bu nesneyle birlikle kullanabileceğimiz override metodlar geliyor, biz OnActionExecuted metodunu kullanacağız, bu metod action çalışmadan önce çalışır ve bize requesti yönetme imkanı verir, metodun içinde yapacağımız işlemler aynı olduğu için tekrar bahsetmeye gerek yok, Fiterlarımız doğrudan actionun üzerine [WhiteIpPermissionFilter] şeklinde yazarak kullanabiliriz ancak bu filterlarımız constructor metodda parametre aldığı için bu şekilde kullanamıyoruz. İlk önce Startup da ConfigureServices de filterlarımızı service scope olarak ekliyoruz;

public void ConfigureServices(IServiceCollection services)
{
	services.Configure<IpList>(Configuration.GetSection("IpList"));
	services.AddControllersWithViews();
	services.AddSingleton<WhiteIpPermissionFilter>();
	services.AddSingleton<BlackIpPermissionFilter>();
}

AddSingleton uygulama ayağa kalkarken nesnenin bir örneğini alır ve her çağırdığızda aynı nesneyi verir. Artık filterimiz kullanılmaya hazır, kullanmak istediğimiz controller ya da actionun üzerine aşağıdaki şekilde yazıp kullanabiliriz.

[ServiceFilter(typeof(WhiteIpPermissionFilter))]
public IActionResult Index()
{
	return View();
}

Son olarak WhiteList ve BlackList kuralları tamamen ayrı senaryolar, projenizde ya WhiteList kullanıp diğer Ip adreslerini engelleyeceksiniz, ya da BlackList kullanıp bunun dışında kalan Ip adreslerine izin vereceksiniz. Aynı şekilde MiddleWare yapısını kullanııyorsanız Filter yapısını kullanmanızada gerek yok :)

Projenin kaynak kodlarına  buradan ulaşabilirsiniz: 

https://github.com/ekremozer/IpPermission