Sécuriser une API .Net Core avec une clé Api (Api Key)

Introduction

Avec le nombre des APIs qui augmente aujourd’hui y a de plus en plus de hackers qui dirigent leur attaque sur ces API alors leur sécurité devient indispensable.

Dans cet article on va voir comment sécuriser une API développée avec .Net Core en implémentant une Clé d’API (API Key).

Démo vidéo

Etape 1 : Paramétrage

Ajouter une clé dans votre fichier de config « appsettings.json », ça sera votre clé d’API, c’est la clé qui doit fournir le client dans l’entête de l’appel HTTP.

"AppSettings": {
    "ApiKey": "12345"
}

Etape 2 : Redéfinir le handler d’authentification

Premièrement créer la class « ApiKeyAuthenticationOptions.cs » qui hérite de « AuthenticationSchemeOptions.cs ».

public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
{
    public const string DefaultHeaderName = "X-Api-Key";

    public string HeaderName { get; set; } = DefaultHeaderName;

    public const string DefaultScheme = "API Key";

    public string Scheme => DefaultScheme;

    public string AuthenticationType = DefaultScheme;
}

Deuxièmement, créer la class « ApiKeyAuthenticationHandler.cs » qui hérite de « AuthenticationHandler ». Ici on doit redéfinir la méthode « HandleAuthenticateAsync() » c’est cette méthode qui va checker la validité de l’Api Key, on récupère notre clé du fichier de configuration (étape 1) et comparer avec la clé fournie par la client appelant dans l’entête de la requête HTTP.

Attention le nom de la clé dans l’entête HTTP doit être le même que celui déclarer dans cette méthode ici c’est : X-Api-Key

public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
{
    private const string ApiKeyHeaderName = "X-Api-Key";

    public ApiKeyAuthenticationHandler(IOptionsMonitor<ApiKeyAuthenticationOptions> options,
                                        ILoggerFactory logger,
                                        UrlEncoder encoder,
                                        ISystemClock clock) : base(options, logger, encoder, clock)
    {

    }


    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var expectedApiKey = Context.RequestServices.GetRequiredService<IOptions<AppSettingsSection>>().Value;

        if (!Request.Headers.TryGetValue(ApiKeyHeaderName, out var apiKeyHeaderValues))
        {
            return AuthenticateResult.NoResult();
        }

        var providedApiKey = apiKeyHeaderValues.FirstOrDefault();

        if (apiKeyHeaderValues.Count == 0 || string.IsNullOrWhiteSpace(providedApiKey))
        {
            return AuthenticateResult.NoResult();
        }

        if (expectedApiKey.ApiKey.Equals(providedApiKey))
        {
            var claims = new List<Claim>
            {
                new Claim("ClaimType", "ClaimValue")
            };
            var identity = new ClaimsIdentity(claims, Options.AuthenticationType);
            var identities = new List<ClaimsIdentity> { identity };
            var principal = new ClaimsPrincipal(identities);
            var ticket = new AuthenticationTicket(principal, Options.Scheme);

            return AuthenticateResult.Success(ticket);
        }

        return AuthenticateResult.Fail("Invalid API Key provided.");
    }
}

Enfin, créer la class statique « AuthenticationBuilderExtensions.cs »

public static class AuthenticationBuilderExtensions
{
    public static AuthenticationBuilder AddApiKeySupport(this AuthenticationBuilder authenticationBuilder, Action<ApiKeyAuthenticationOptions> options)
    {
        return authenticationBuilder.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(ApiKeyAuthenticationOptions.DefaultScheme, options);
    }
}

Etape 3 : Intégration à Swagger

Si votre API intègre Swagger vous devez configurer ce dernier pour la prise en compte de votre Api Key lors des appels depuis son interface

Ajouter le code suivant en suite à votre configuration Swagger.

services.AddSwaggerGen(c =>
{
 //...
  var security = new Dictionary<string, IEnumerable<string>>
    {
        {"ApiKeyAuth", new string[] { }},
    };

    c.AddSecurityDefinition("ApiKeyAuth", new OpenApiSecurityScheme
    {
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey,
        Name = "X-Api-Key",
        Description = $"Standard Api Key header authentication."
    });

    c.AddSecurityRequirement(new OpenApiSecurityRequirement()
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "ApiKeyAuth"
                },
                Scheme = "API Key",
                Name = "X-Api-Key",
                In = ParameterLocation.Header,
            },
            new List<string>()
        }
    });
});

Etape 4 : le startup

Déclarer l’authentification dans le startup.cs dans la méthode ConfigureServices() et Configure ()

public void ConfigureServices(IServiceCollection services)
{
    //...
    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = ApiKeyAuthenticationOptions.DefaultScheme;
        options.DefaultChallengeScheme = ApiKeyAuthenticationOptions.DefaultScheme;
    
    }).AddApiKeySupport(options => { });
    
    services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    //...
    
    app.UseAuthentication();
    app.UseAuthorization();
    
    //...
}

Etape 5 : la touche finale

Ajouter l’attribut [Authorize] sur les actions de vos contrôleur que vous voulez sécuriser par la clé Api.

[Authorize]
[HttpGet]
[Route("ping")]
public IActionResult Ping(string mode)
{
    if (mode == "ok")
    {
        return Ok();
    }
    else
    {
        return StatusCode(StatusCodes.Status500InternalServerError);
    }
}

Etape 6 : Test

Vous pouvez utiliser Postman ou tout autre client pour tester votre Api sécurisée, afin de le faire ajouter une clé dans l’entête de votre requête avec le même nom que vous avez spécifié à l’étape 2.

Démo vidéo

Soyez le premier à commenter

Poster un Commentaire

Votre adresse de messagerie ne sera pas publiée.


*