×

iFour Logo

How to secure public APIs in ASP.NET Core?

Kapil Panchal - November 30, 2020

Listening is fun too.

Straighten your back and cherish with coffee - PLAY !

  • play
  • pause
  • pause
How to secure public APIs in ASP.NET Core?

ASP.NET Core is a popular structure. Its key advantages include features such as cross-platform execution, high performance, built-in dependency injection and modular HTTP request pipeline.

Challenges


The ASP.NET Core provides support for many authentication providers to secure applications through numerous authentication workflows. However, in many scenarios, we have to provide a web application/site that is based on an unauthenticated API with anonymous access.

For example, we have a list of products in the database and we want to display these products on a web page. We can write an API to provide a list of products and the front end (web site) can receive this list through the API and display it on our public products web page.

Without applying a level of security, such architectures can be an open security vulnerability to exploitation.

Available Security Controls in ASP.NET


ASP.NET Provides solutions for common vulnerabilities including core

  • Cross-site scripting
  • SQL injection,
  • Cross-Site Request Forgery (CSRF)
  • Open redirects

Going a step further


As developers, we should also protect our applications from other common attack vectors including

  • Distributed denial-of-service (DDOS)
  • Denial-of-service (DOS)
  • Bulk data egress
  • Probe response
  • Scraping

The two steps we can take careof to verify the referrer header and rate-limiting, discussed below in detail.

Use IP based request limit action filter


We can limit customers to a certain number of requests over a specified period of time to prevent malicious bot attacks.We havecreated IP based requestlimitactionfilter in the ASP.NET Core. Keep in mind that multiple clients can sit behind a single IP address so you can meet this within your limits, or combine the IP address with other request data to make requests more unique.

To try the filter, you just need to add an ActionAttribute at the top of the controller action.

 
[HttpGet()]
[ValidateReferrer]
[RequestLimit("Test-Action", NoOfRequest = 3, Seconds = 10)]
publicasync TaskGetAsync(CancellationTokenct)
{
// code here  
}
			

Here is the implementation of the filter:

			namespace Security.Api.Filters
{
    using System;
    using System.Net;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Filters;
    using Microsoft.Extensions.Caching.Memory;
    [AttributeUsage(AttributeTargets.Method)]
    public class RequestAttribute :ActionFilterAttribute
    {
        public RequestAttribute(string name)
        {
            Name = name;
        }
        public string Name
        {
            get;
        }
        public intNoOfRequest
        {
            get;
            set;
        } = 1;
        public int Seconds
        {
            get;
            set;
        } = 1;
        private static MemoryCachememoryCache
        {
            get;
        } = new MemoryCache(new MemoryCacheOptions());
        public override void OnActionExecuting(ActionExecutingContext context)
        {
varipAddress = context.HttpContext.Request.HttpContext.Connection.RemoteIpAddress;
varmemoryCacheKey = $ "{Name}-{ipAddress}";
memoryCache.TryGetValue(memoryCacheKey, out intprevReqCount);
            if (prevReqCount>= NoOfRequest)
            {
context.Result = new ContentResult
                {
                    Content = $ "Request is exceeded. Try again in seconds.",
                };
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.TooManyRequests;
            }
            else
            {
varcacheEntryOptions = new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(Seconds));
memoryCache.Set(memoryCacheKey, (prevReqCount + 1), cacheEntryOptions);
            }
        }
    }
}

Add referrercheck action filter


To protect the API from abuse and to provide additional protection against Cross-Site Request Forgery (CSRF) attacks, security checks are performed on the request referrer header for each REST API request sent to the server.

This API validates where the request comes from. We have created a Referrer Check Action Filter in ASP.NET Core. It prevents access to tools like POSTMEN, REST client, etc.

You just need to do is add an ActionAttribute to the top of the controller Action.

[HttpGet()]
[ValidateReferrer]
publicasync TaskGetAsync(CancellationTokenct)
{
// your code here  
}
			

Here is the implementation of the filter

 
namespace Security.Api.Filters
{
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Filters;
    using Microsoft.Extensions.Configuration;
    using System;
    using System.Linq;
    using System.Net;
    [AttributeUsage(AttributeTargets.Method)]
    public sealed class ValidateAttribute :ActionFilterAttribute
    {
        private IConfiguration _configuration;
        public ValidateAttribute() { }
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            _configuration = (IConfiguration)context.HttpContext.RequestServices.GetService(typeof(IConfiguration));
base.OnActionExecuting(context);
            if (!IsValidRequest(context.HttpContext.Request))
            {
context.Result = new ContentResult
                {
                    Content = $ "Invalid header"
                };
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.ExpectationFailed;
            }
        }
        private bool IsValidRequest(HttpRequest request)
        {
            string referrerURL = "";
            if (request.Headers.ContainsKey("Referer"))
            {
referrerURL = request.Headers["Referer"];
            }
            if (string.IsNullOrWhiteSpace(referrerURL)) return false;
           //Allows to check customer list
varUrls = _configuration.GetSection("CorsOrigin").Get()?.Select(url => new Uri(url).Authority).ToList();
            //add current host for swagger calls    
var host = request.Host.Value;
Urls.Add(host);
            bool isValidClient = Urlsl.Contains(new Uri(referrerURL).Authority);
            // comapre with base uri
            return isValidClient;
        }
    }
}



		
		

Searching for Dedicated ASP.Net Core Web Developer ? Your Search ends here.

Add DoSattack middleware


If you have the auto scale configured, DOS attacks overwhelm your APIs, making them unauthorized and/or expensive. There are various ways to avoid this problem by request throttling. There is an option here to use intermediaries to restrict the number of requests from particulate client IP addresses.

Below is the code for DosAttackMiddleware.cs

 namespace Security.Api.Middlewares
{
    using Microsoft.AspNetCore.Http;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Threading.Tasks;
    using System.Timers;
    public sealed class DosAttackMiddleware
    {
        private static IDictionary _IpAdresses = new Dictionary();
        private static Stack _Banded = new Stack();
        private static Timer _Timer = CreateTimer();
        private static Timer _BannedTimer = CreateBanningTimer();
        private
        const int BANNED_REQUESTS = 10;
        private
        const int REDUCTION_INTERVAL = 1000;
        private
        const int RELEASE_INTERVAL = 3 * 60 * 1000; // 3 minutes    
        private RequestDelegate _next;
        public DosAttackMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        public async Task InvokeAsync(HttpContexthttpContext)
        {
            string ip = httpContext.Connection.RemoteIpAddress.ToString();
            if (_Banned.Contains(ip))
            {
httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
            }
CheckIpAddress(ip);
            await _next(httpContext);
        }
        private static void CheckIpAddress(string ip)
        {
            if (!_IpAdresses.ContainsKey(ip))
            {
                _IpAdresses[ip] = 1;
            }
            else if (_IpAdresses[ip] == BANNED_REQUESTS)
            {
                _Banned.Push(ip);
                _IpAdresses.Remove(ip);
            }
            else
            {
                _IpAdresses[ip]++;
            }
        }
        private static Timer CreateTimer()
        {
            Timer timer = GetTimer(REDUCTION_INTERVAL);
timer.Elapsed += new ElapsedEventHandler(TimerElapsed);
            return timer;
        }
        private static Timer CreateTimer()
        {
            Timer timer = GetTimer(RELEASE_INTERVAL);
timer.Elapsed += delegate {
                if (_Banned.Any()) _Banned.Pop();
            };
            return timer;
        }
        private static Timer GetTimer(int interval)
        {
            Timer timer = new Timer();
timer.Interval = interval;
timer.Start();
            return timer;
        }
        private static TimerElapsed(object sender, ElapsedEventArgs e)
        {
            foreach (string key in _IpAdresses.Keys.ToList())
            {
                _IpAdresses[key]--;
                if (_IpAdresses[key] == 0) _IpAdresses.Remove(key);
            }
        }
    }
}
			

Conclusion


An unauthorized API is open to abuse. We should prevent the explicit attack vector by adding additional code. Hopefully this blog makes it easier to enforce these restrictions while making the lives of these attackers more difficult.

How to secure public APIs in ASP.NET Core? ASP.NET Core is a popular structure. Its key advantages include features such as cross-platform execution, high performance, built-in dependency injection and modular HTTP request pipeline. Table of Content 1. Challenges 2. Available Security Controls in ASP.NET 3. Going a step further 4. Use IP based request limit action filter 5. Add referrercheck action filter 6. Add DoSattack middleware 7. Conclusion Challenges The ASP.NET Core provides support for many authentication providers to secure applications through numerous authentication workflows. However, in many scenarios, we have to provide a web application/site that is based on an unauthenticated API with anonymous access. For example, we have a list of products in the database and we want to display these products on a web page. We can write an API to provide a list of products and the front end (web site) can receive this list through the API and display it on our public products web page. Without applying a level of security, such architectures can be an open security vulnerability to exploitation. Available Security Controls in ASP.NET ASP.NET Provides solutions for common vulnerabilities including core Cross-site scripting SQL injection, Cross-Site Request Forgery (CSRF) Open redirects Going a step further As developers, we should also protect our applications from other common attack vectors including Distributed denial-of-service (DDOS) Denial-of-service (DOS) Bulk data egress Probe response Scraping The two steps we can take careof to verify the referrer header and rate-limiting, discussed below in detail. Use IP based request limit action filter We can limit customers to a certain number of requests over a specified period of time to prevent malicious bot attacks.We havecreated IP based requestlimitactionfilter in the ASP.NET Core. Keep in mind that multiple clients can sit behind a single IP address so you can meet this within your limits, or combine the IP address with other request data to make requests more unique. To try the filter, you just need to add an ActionAttribute at the top of the controller action. Read More: Asp.net Core Api With Entity Framework   [HttpGet()] [ValidateReferrer] [RequestLimit("Test-Action", NoOfRequest = 3, Seconds = 10)] publicasync TaskGetAsync(CancellationTokenct) { // code here } Here is the implementation of the filter: namespace Security.Api.Filters { using System; using System.Net; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Caching.Memory; [AttributeUsage(AttributeTargets.Method)] public class RequestAttribute :ActionFilterAttribute { public RequestAttribute(string name) { Name = name; } public string Name { get; } public intNoOfRequest { get; set; } = 1; public int Seconds { get; set; } = 1; private static MemoryCachememoryCache { get; } = new MemoryCache(new MemoryCacheOptions()); public override void OnActionExecuting(ActionExecutingContext context) { varipAddress = context.HttpContext.Request.HttpContext.Connection.RemoteIpAddress; varmemoryCacheKey = $ "{Name}-{ipAddress}"; memoryCache.TryGetValue(memoryCacheKey, out intprevReqCount); if (prevReqCount>= NoOfRequest) { context.Result = new ContentResult { Content = $ "Request is exceeded. Try again in seconds.", }; context.HttpContext.Response.StatusCode = (int)HttpStatusCode.TooManyRequests; } else { varcacheEntryOptions = new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(Seconds)); memoryCache.Set(memoryCacheKey, (prevReqCount + 1), cacheEntryOptions); } } } } Add referrercheck action filter To protect the API from abuse and to provide additional protection against Cross-Site Request Forgery (CSRF) attacks, security checks are performed on the request referrer header for each REST API request sent to the server. This API validates where the request comes from. We have created a Referrer Check Action Filter in ASP.NET Core. It prevents access to tools like POSTMEN, REST client, etc. You just need to do is add an ActionAttribute to the top of the controller Action. [HttpGet()] [ValidateReferrer] publicasync TaskGetAsync(CancellationTokenct) { // your code here } Here is the implementation of the filter   namespace Security.Api.Filters { using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Configuration; using System; using System.Linq; using System.Net; [AttributeUsage(AttributeTargets.Method)] public sealed class ValidateAttribute :ActionFilterAttribute { private IConfiguration _configuration; public ValidateAttribute() { } public override void OnActionExecuting(ActionExecutingContext context) { _configuration = (IConfiguration)context.HttpContext.RequestServices.GetService(typeof(IConfiguration)); base.OnActionExecuting(context); if (!IsValidRequest(context.HttpContext.Request)) { context.Result = new ContentResult { Content = $ "Invalid header" }; context.HttpContext.Response.StatusCode = (int)HttpStatusCode.ExpectationFailed; } } private bool IsValidRequest(HttpRequest request) { string referrerURL = ""; if (request.Headers.ContainsKey("Referer")) { referrerURL = request.Headers["Referer"]; } if (string.IsNullOrWhiteSpace(referrerURL)) return false; //Allows to check customer list varUrls = _configuration.GetSection("CorsOrigin").Get()?.Select(url => new Uri(url).Authority).ToList(); //add current host for swagger calls var host = request.Host.Value; Urls.Add(host); bool isValidClient = Urlsl.Contains(new Uri(referrerURL).Authority); // comapre with base uri return isValidClient; } } } Searching for Dedicated ASP.Net Core Web Developer ? Your Search ends here. See here Add DoSattack middleware If you have the auto scale configured, DOS attacks overwhelm your APIs, making them unauthorized and/or expensive. There are various ways to avoid this problem by request throttling. There is an option here to use intermediaries to restrict the number of requests from particulate client IP addresses. Below is the code for DosAttackMiddleware.cs namespace Security.Api.Middlewares { using Microsoft.AspNetCore.Http; using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading.Tasks; using System.Timers; public sealed class DosAttackMiddleware { private static IDictionary _IpAdresses = new Dictionary(); private static Stack _Banded = new Stack(); private static Timer _Timer = CreateTimer(); private static Timer _BannedTimer = CreateBanningTimer(); private const int BANNED_REQUESTS = 10; private const int REDUCTION_INTERVAL = 1000; private const int RELEASE_INTERVAL = 3 * 60 * 1000; // 3 minutes private RequestDelegate _next; public DosAttackMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContexthttpContext) { string ip = httpContext.Connection.RemoteIpAddress.ToString(); if (_Banned.Contains(ip)) { httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; } CheckIpAddress(ip); await _next(httpContext); } private static void CheckIpAddress(string ip) { if (!_IpAdresses.ContainsKey(ip)) { _IpAdresses[ip] = 1; } else if (_IpAdresses[ip] == BANNED_REQUESTS) { _Banned.Push(ip); _IpAdresses.Remove(ip); } else { _IpAdresses[ip]++; } } private static Timer CreateTimer() { Timer timer = GetTimer(REDUCTION_INTERVAL); timer.Elapsed += new ElapsedEventHandler(TimerElapsed); return timer; } private static Timer CreateTimer() { Timer timer = GetTimer(RELEASE_INTERVAL); timer.Elapsed += delegate { if (_Banned.Any()) _Banned.Pop(); }; return timer; } private static Timer GetTimer(int interval) { Timer timer = new Timer(); timer.Interval = interval; timer.Start(); return timer; } private static TimerElapsed(object sender, ElapsedEventArgs e) { foreach (string key in _IpAdresses.Keys.ToList()) { _IpAdresses[key]--; if (_IpAdresses[key] == 0) _IpAdresses.Remove(key); } } } } Conclusion An unauthorized API is open to abuse. We should prevent the explicit attack vector by adding additional code. Hopefully this blog makes it easier to enforce these restrictions while making the lives of these attackers more difficult.

Build Your Agile Team

Enter your e-mail address Please enter valid e-mail

Categories

Ensure your sustainable growth with our team

Talk to our experts
Sustainable
Sustainable
 

Blog Our insights

Next-Gen Programming Languages: Shaping the Future of Software Development in 2024
Next-Gen Programming Languages: Shaping the Future of Software Development in 2024

Introduction Imagine standing in line at the grocery store, waiting to pay for groceries. You pull out your phone and scan each item’s barcode with a single tap. This seemingly...

MySQL vs Azure SQL Database: Understanding Needs, Factors, and Performance Metrics
MySQL vs Azure SQL Database: Understanding Needs, Factors, and Performance Metrics

The world of technology is constantly changing, and databases are at the forefront of this evolution. We have explored different types of databases, both physical and cloud-based, and realized how each of them provides unique features to improve data accessibility and inclusive performance. Leading the pack are MySQL and Azure SQL database services , helping business elevate their processes to new heights.

Streamlining E-commerce Operations with Microsoft PowerApps
Streamlining E-commerce Operations with Microsoft PowerApps

In today's rapidly changing digital world, eCommerce is a dynamic industry. Every day, millions of transactions take place online, so companies are always looking for new and creative methods to improve consumer satisfaction and optimize operations.