Partager via


Sécuriser les microservices et les applications web .NET

Conseil / Astuce

Ce contenu est un extrait du livre .NET Microservices Architecture for Containerized .NET Applications, disponible sur .NET Docs ou en tant que PDF téléchargeable gratuitement pour une lecture hors ligne.

Miniature de la couverture du livre électronique sur l'architecture de microservices .NET pour les applications .NET conteneurisées.

Il existe tellement d’aspects sur la sécurité dans les microservices et les applications web que le sujet pourrait facilement prendre plusieurs livres comme celui-ci. Dans cette section, nous allons nous concentrer sur l’authentification, l’autorisation et les secrets d’application.

Implémenter l’authentification dans les microservices et les applications web .NET

Il est souvent nécessaire que les ressources et les API publiées par un service soient limitées à certains utilisateurs ou clients approuvés. La première étape pour prendre ces types de décisions d’approbation au niveau de l’API est l’authentification. L’authentification est le processus de vérification fiable de l’identité d’un utilisateur.

Dans les scénarios de microservices, l’authentification est généralement gérée de manière centralisée. Si vous utilisez une passerelle API, la passerelle est un bon endroit pour s’authentifier, comme illustré dans la figure 9-1. Si vous utilisez cette approche, assurez-vous que les microservices individuels ne peuvent pas être atteints directement (sans la passerelle API), sauf si une sécurité supplémentaire est en place pour authentifier les messages qu’ils proviennent de la passerelle ou non.

Diagramme montrant comment l’application mobile cliente interagit avec le serveur principal.

Figure 9-1. Authentification centralisée avec une passerelle API

Lorsque la passerelle API centralise l’authentification, elle ajoute des informations utilisateur lors du transfert de demandes aux microservices. Si des services sont accessibles directement, un service d’authentification tel qu’Azure Active Directory ou un microservice d’authentification dédié agissant en tant que service de jeton de sécurité (STS) peut être utilisé pour authentifier les utilisateurs. Les décisions d’approbation sont partagées entre les services à l’aide de jetons de sécurité ou de cookies. (Ces jetons peuvent être partagés entre les applications ASP.NET Core, si nécessaire, en implémentant le partage de cookies.) Ce modèle est illustré dans la figure 9-2.

Diagramme montrant l’authentification via les microservices principaux.

Figure 9-2. Authentification par microservice d’identité ; l’approbation est partagée à l’aide d’un jeton d’autorisation

Lorsque les microservices sont accessibles directement, l’approbation, qui inclut l’authentification et l’autorisation, est gérée par un jeton de sécurité émis par un microservice dédié, partagé entre les microservices.

S’authentifier avec ASP.NET Identité principale

Le mécanisme principal dans ASP.NET Core pour identifier les utilisateurs d’une application est le système d’appartenance ASP.NET Core Identity . ASP.NET Core Identity stocke les informations utilisateur (notamment les revendications, les rôles et les informations de connexion) dans un magasin de données configuré par le développeur. En règle générale, le magasin de données ASP.NET Core Identity est un magasin Entity Framework fourni dans le Microsoft.AspNetCore.Identity.EntityFrameworkCore package. Toutefois, des magasins personnalisés ou d’autres packages tiers peuvent être utilisés pour stocker des informations d’identité dans Stockage Table Azure, CosmosDB ou d’autres emplacements.

Conseil / Astuce

ASP.NET Core 2.1 et versions ultérieures fournit ASP.NET Core Identity sous forme de bibliothèque de classes Razor, vous ne verrez donc pas beaucoup du code nécessaire dans votre projet, comme c’était le cas pour les versions précédentes. Pour plus d’informations sur la façon de personnaliser le code d’identité en fonction de vos besoins, consultez L’identité de structure dans ASP.NET projets Core.

Le code suivant est extrait du modèle de projet MVC d’application web principale ASP.NET avec l’authentification de compte d’utilisateur individuelle sélectionnée. Il montre comment configurer ASP.NET Core Identity à l’aide d’Entity Framework Core dans le fichier Program.cs .

//...
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(
        builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddDefaultIdentity<IdentityUser>(options =>
    options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddRazorPages();
//...

Une fois ASP.NET Core Identity configurée, vous l'activez en ajoutant app.UseAuthentication() et endpoints.MapRazorPages() dans le fichier Program.cs du service.

//...
app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapRazorPages();
});
//...

Importante

Les lignes du code précédent DOIVENT ÊTRE DANS L’ORDRE INDIQUÉ pour que l’identité fonctionne correctement.

L’utilisation de ASP.NET Core Identity permet plusieurs scénarios :

  • Créez des informations utilisateur à l’aide du type UserManager (userManager.CreateAsync).

  • Authentifiez les utilisateurs à l’aide du type SignInManager. Vous pouvez utiliser signInManager.SignInAsync pour vous connecter directement ou signInManager.PasswordSignInAsync pour confirmer que le mot de passe de l’utilisateur est correct, puis les connecter.

  • Identifiez un utilisateur en fonction des informations stockées dans un cookie (qui est lu par ASP.NET middleware Core Identity) afin que les demandes suivantes d’un navigateur incluent l’identité et les revendications de l’utilisateur connecté.

ASP.NET Core Identity prend également en charge l’authentification à deux facteurs.

Pour les scénarios d’authentification qui utilisent un magasin de données utilisateur local et qui conservent l’identité entre les requêtes utilisant des cookies (comme c’est le cas pour les applications web MVC), ASP.NET Core Identity est une solution recommandée.

S’authentifier auprès de fournisseurs externes

ASP.NET Core prend également en charge l’utilisation de fournisseurs d’authentification externes pour permettre aux utilisateurs de se connecter via des flux OAuth 2.0 . Cela signifie que les utilisateurs peuvent se connecter à l’aide de processus d’authentification existants à partir de fournisseurs tels que Microsoft, Google, Facebook ou Twitter et associer ces identités à une identité ASP.NET Core dans votre application.

Pour utiliser l’authentification externe, en plus d’inclure l’intergiciel d’authentification comme mentionné précédemment, à l’aide de la app.UseAuthentication() méthode, vous devez également inscrire le fournisseur externe dans Program.cs , comme indiqué dans l’exemple suivant :

//...
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();

services.AddAuthentication()
    .AddMicrosoftAccount(microsoftOptions =>
    {
        microsoftOptions.ClientId = builder.Configuration["Authentication:Microsoft:ClientId"];
        microsoftOptions.ClientSecret = builder.Configuration["Authentication:Microsoft:ClientSecret"];
    })
    .AddGoogle(googleOptions => { ... })
    .AddTwitter(twitterOptions => { ... })
    .AddFacebook(facebookOptions => { ... });
//...

Les fournisseurs d’authentification externes populaires et leurs packages NuGet associés sont indiqués dans le tableau suivant :

Fournisseur Paquet
Microsoft Microsoft.AspNetCore.Authentication.MicrosoftAccount
Google Microsoft.AspNetCore.Authentication.Google
Facebook Microsoft.AspNetCore.Authentication.Facebook
Gazouiller Microsoft.AspNetCore.Authentication.Twitter

Dans tous les cas, vous devez effectuer une procédure d’inscription d’application dépendante du fournisseur et qui implique généralement :

  1. Obtention d’un ID d’application cliente.
  2. Obtention d’une clé secrète d’application cliente.
  3. Configuration d’une URL de redirection, gérée par l’intergiciel d’autorisation et le fournisseur inscrit
  4. Il est possible de configurer une URL de déconnexion pour gérer correctement la déconnexion dans un scénario de connexion unique (SSO).

Pour plus d’informations sur la configuration de votre application pour un fournisseur externe, consultez la documentation ASP.NET Core sur l’authentification du fournisseur externe.

Conseil / Astuce

Tous les détails sont gérés par le middleware d’autorisation et les services mentionnés précédemment. Par conséquent, vous devez simplement choisir l’option d’authentification de compte d’utilisateur individuel lorsque vous créez le projet d’application web ASP.NET Core dans Visual Studio, comme illustré dans la figure 9-3, en plus d’inscrire les fournisseurs d’authentification mentionnés précédemment.

Capture d’écran de la boîte de dialogue New ASP.NET Core Web Application.

Figure 9-3. Sélection de l’option Comptes d’utilisateur individuels, pour l’utilisation de l’authentification externe, lors de la création d’un projet d’application web dans Visual Studio 2019.

En plus des fournisseurs d’authentification externes répertoriés précédemment, des packages tiers sont disponibles pour fournir un intergiciel pour utiliser de nombreux fournisseurs d’authentification externes. Pour obtenir une liste, consultez le référentiel AspNet.Security.OAuth.Providers sur GitHub.

Vous pouvez également créer votre propre intergiciel d’authentification externe pour résoudre certains besoins spéciaux.

S’authentifier avec des jetons du porteur

L’authentification avec ASP.NET Identité principale (ou Identité et fournisseurs d’authentification externes) fonctionne bien pour de nombreux scénarios d’application web dans lesquels le stockage des informations utilisateur dans un cookie est approprié. Dans d’autres scénarios, cependant, les cookies ne sont pas un moyen naturel de conserver et de transmettre des données.

Par exemple, dans une API web principale ASP.NET qui expose des points de terminaison RESTful accessibles par des applications monopage (SPA), par des clients natifs ou même par d’autres API web, vous souhaitez généralement utiliser l’authentification par jeton du porteur à la place. Ces types d’applications ne fonctionnent pas avec des cookies, mais peuvent facilement récupérer un jeton du porteur et l’inclure dans l’en-tête d’autorisation des demandes suivantes. Pour activer l’authentification par jeton, ASP.NET Core prend en charge plusieurs options d’utilisation d’OAuth 2.0 et OpenID Connect.

S’authentifier avec un fournisseur d’identité OpenID Connect ou OAuth 2.0

Si des informations utilisateur sont stockées dans Azure Active Directory ou une autre solution d’identité prenant en charge OpenID Connect ou OAuth 2.0, vous pouvez utiliser le package Microsoft.AspNetCore.Authentication.OpenIdConnect pour vous authentifier à l’aide du flux de travail OpenID Connect. Par exemple, pour vous authentifier auprès du microservice Identity.Api dans eShopOnContainers, une application web ASP.NET Core peut utiliser l’intergiciel à partir de ce package, comme illustré dans l’exemple simplifié suivant dans Program.cs :

// Program.cs

var identityUrl = builder.Configuration.GetValue<string>("IdentityUrl");
var callBackUrl = builder.Configuration.GetValue<string>("CallBackUrl");
var sessionCookieLifetime = builder.Configuration.GetValue("SessionCookieLifetimeMinutes", 60);

// Add Authentication services

services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddCookie(setup => setup.ExpireTimeSpan = TimeSpan.FromMinutes(sessionCookieLifetime))
.AddOpenIdConnect(options =>
{
    options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.Authority = identityUrl.ToString();
    options.SignedOutRedirectUri = callBackUrl.ToString();
    options.ClientId = useLoadTest ? "mvctest" : "mvc";
    options.ClientSecret = "secret";
    options.ResponseType = useLoadTest ? "code id_token token" : "code id_token";
    options.SaveTokens = true;
    options.GetClaimsFromUserInfoEndpoint = true;
    options.RequireHttpsMetadata = false;
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("orders");
    options.Scope.Add("basket");
    options.Scope.Add("marketing");
    options.Scope.Add("locations");
    options.Scope.Add("webshoppingagg");
    options.Scope.Add("orders.signalrhub");
});

// Build the app
//…
app.UseAuthentication();
//…
app.UseEndpoints(endpoints =>
{
    //...
});

Lorsque vous utilisez ce flux de travail, le middleware ASP.NET Core Identity n’est pas nécessaire, car tous les stockages et l’authentification des informations utilisateur sont gérés par le service Identity.

Émettre des jetons de sécurité à partir d’un service ASP.NET Core

Si vous préférez émettre des jetons de sécurité pour les utilisateurs locaux ASP.NET Core Identity plutôt que d’utiliser un fournisseur d’identité externe, vous pouvez tirer parti de certaines bonnes bibliothèques tierces.

IdentityServer4 et OpenIddict sont des fournisseurs OpenID Connect qui s’intègrent facilement à ASP.NET Core Identity pour vous permettre d’émettre des jetons de sécurité à partir d’un service ASP.NET Core. La documentation IdentityServer4 contient des instructions détaillées sur l’utilisation de la bibliothèque. Toutefois, les étapes de base pour utiliser IdentityServer4 pour émettre des jetons sont les suivantes.

  1. Vous configurez IdentityServer4 dans Program.cs en appelant le générateur. Services.AddIdentityServer.

  2. Vous appelez l’application. UseIdentityServer dans Program.cs pour ajouter IdentityServer4 au pipeline de traitement des requêtes HTTP de l’application. Cela permet à la bibliothèque de traiter les demandes adressées aux points de terminaison OpenID Connect et OAuth2 tels que /connect/token.

  3. Vous configurez le serveur d’identité en définissant les données suivantes :

    • Informations d’identification à utiliser pour la signature.

    • Ressources d’identité et d’API auxquelles les utilisateurs peuvent demander l’accès :

      • Les ressources d’API représentent des données ou des fonctionnalités protégées auxquelles un utilisateur peut accéder avec un jeton d’accès. Un exemple de ressource d’API serait une API web (ou un ensemble d’API) qui nécessite une autorisation.

      • Les ressources d'identité représentent des informations (déclarations) fournies à un client pour identifier un utilisateur. Les revendications peuvent inclure le nom d’utilisateur, l’adresse e-mail, et ainsi de suite.

    • Clients qui se connectent pour demander des jetons.

    • Mécanisme de stockage pour les informations utilisateur, comme ASP.NET Identité principale ou une alternative.

Lorsque vous spécifiez des clients et des ressources à utiliser pour IdentityServer4, vous pouvez passer une IEnumerable<T> collection du type approprié aux méthodes qui prennent des magasins de clients ou de ressources en mémoire. Ou, pour des scénarios plus complexes, vous pouvez utiliser l'injection de dépendances pour fournir des types de client ou de fournisseur de ressources.

Un exemple de configuration pour IdentityServer4 pour utiliser des ressources en mémoire et des clients fournis par un type IClientStore personnalisé peut ressembler à l’exemple suivant :

// Program.cs

builder.Services.AddSingleton<IClientStore, CustomClientStore>();
builder.Services.AddIdentityServer()
    .AddSigningCredential("CN=sts")
    .AddInMemoryApiResources(MyApiResourceProvider.GetAllResources())
    .AddAspNetIdentity<ApplicationUser>();
//...

Consommer des jetons de sécurité

L’authentification auprès d’un point de terminaison OpenID Connect ou l’émission de vos propres jetons de sécurité couvre certains scénarios. Mais qu’en est-il d’un service qui doit simplement limiter l’accès à ceux qui ont des jetons de sécurité valides fournis par un autre service ?

Pour ce scénario, l’intergiciel d’authentification qui gère les jetons JWT est disponible dans le package Microsoft.AspNetCore.Authentication.JwtBearer . JWT correspond à « JSON Web Token » et est un format de jeton de sécurité commun (défini par RFC 7519) pour communiquer des revendications de sécurité. Un exemple simplifié d’utilisation du middleware pour consommer de tels jetons peut ressembler à ce fragment de code, extrait du microservice Ordering.Api d’eShopOnContainers.

// Program.cs

var identityUrl = builder.Configuration.GetValue<string>("IdentityUrl");

// Add Authentication services

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme;

}).AddJwtBearer(options =>
{
    options.Authority = identityUrl;
    options.RequireHttpsMetadata = false;
    options.Audience = "orders";
});

// Build the app

app.UseAuthentication();
//…
app.UseEndpoints(endpoints =>
{
    //...
});

Les paramètres de cette utilisation sont les suivants :

  • Audience représente le récepteur du jeton entrant ou la ressource à laquelle le jeton accorde l’accès. Si la valeur spécifiée dans ce paramètre ne correspond pas au paramètre dans le jeton, le jeton est rejeté.

  • Authority est l’adresse du serveur d’authentification émettrice de jetons. Le middleware d’authentification du porteur JWT utilise cet URI pour obtenir la clé publique qui peut être utilisée pour valider la signature du jeton. L’intergiciel confirme également que le iss paramètre dans le jeton correspond à cet URI.

Un autre paramètre, RequireHttpsMetadataest utile à des fins de test ; vous définissez ce paramètre sur false afin de pouvoir effectuer des tests dans des environnements où vous n’avez pas de certificats. Dans les déploiements réels, les jetons du porteur JWT doivent toujours être transmis uniquement via HTTPS.

Avec ce middleware en place, les jetons JWT sont automatiquement extraits des en-têtes d’autorisation. Ils sont ensuite désérialisés, validés (en utilisant les valeurs des paramètres Audience et Authority), et stockés en tant qu'informations utilisateur qui pourront être référencées ultérieurement par les actions ou les filtres d'autorisation MVC.

Le middleware d’authentification du porteur JWT peut également prendre en charge des scénarios plus avancés, tels que l’utilisation d’un certificat local pour valider un jeton si l’autorité n’est pas disponible. Pour ce scénario, vous pouvez spécifier un TokenValidationParameters objet dans l’objet JwtBearerOptions .

Ressources supplémentaires