Sécurisation de l'API Web ASP.NET

Une fois que votre API Web est développée, avant de l'exposer à vos clients, en fonction de vos besoins, vous devrez peut-être sécuriser tout ou partie de votre service API afin que seuls les utilisateurs vérifiés puissent accéder à votre service API. Cette sécurisation dans ASP.NET peut être réalisée à l'aide des mécanismes d'authentification et d'autorisation..

Authentification

L'authentification est le processus permettant de déterminer si une personne ou une chose est en réalité qui ou ce qu'elle prétend être. En utilisant le mécanisme d'authentification, nous nous assurons que chaque demande reçue par le service API Web est envoyée par un client avec les informations d'identification appropriées..

Authentification à l'aide de gestionnaires de messages

UNE gestionnaire de message est une classe qui reçoit une requête HTTP et renvoie une réponse HTTP. Les gestionnaires de messages sont des classes dérivées de la classe abstraite HttpMessageHandler. Ils conviennent aux problèmes intersectoriels opérant au niveau des messages HTTP (plutôt que des actions du contrôleur). Par exemple, un gestionnaire de messages peut:

  • lire ou modifier les en-têtes de requête
  • ajouter un en-tête de réponse aux réponses
  • valider les demandes avant qu'elles n'atteignent le contrôleur

Dans une API Web, généralement, une série de gestionnaires de messages sont enchaînés, formant un motif appelé déléguer gestionnaire.

L'ordre dans lequel ces gestionnaires sont configurés est important car ils seront exécutés séquentiellement. 

Le gestionnaire le plus important se trouve tout en haut, surveillant tout ce qui entre. Si les vérifications sont satisfaisantes, il transmettra cette demande au gestionnaire de délégation suivant, et ainsi de suite.. 

Si tout se passe bien, il arrivera ensuite au contrôleur API et exécutera l'action souhaitée. Toutefois, si l'une des vérifications échoue dans les gestionnaires, la demande est refusée et une réponse est envoyée au client..

Avec autant de théorie en main, écrivons maintenant du code pour nos gestionnaires. Nous allons créer deux gestionnaires de messages dans cet article:

  1. APIKeyHandler: Gestionnaire chargé d'intercepter une requête HTTP et de s'assurer que son en-tête contient une clé API
  2. AuthHandler: Gestionnaire responsable de l'authentification des informations d'identification et des rôles d'un utilisateur

Authentification par clé API

Dans votre projet Web API, créez un dossier appelé MessageHandlerset ajouter une classe APIKeyHandler.cs.

Classe publique APIKeyHandler: DelegatingHandler // définit une clé d'API par défaut chaîne privée const yourApiKey = "X-some-key"; tâche de remplacement asynchrone protégée SendAsync (demande HttpRequestMessage, CancellationToken cancelToken) bool isValidAPIKey = false; IEnumerable lsHeaders; // Valide que la clé api existe var checkApiKeyExists = request.Headers.TryGetValues ​​("API_KEY", out lsHeaders); if (checkApiKeyExists) if (lsHeaders.FirstOrDefault (). Equals (votreApiKey)) isValidAPIKey = true;  // Si la clé n'est pas valide, retournez un code de statut http. if (! isValidAPIKey) renvoie la requête.CreateResponse (HttpStatusCode.Forbidden, "Clé API incorrecte"); // Autorise la demande à traiter plus en aval dans le pipeline var response = wait base.SendAsync (request, cancelToken); // Renvoie la réponse en arrière dans la chaîne retour réponse; 

le APIKeyHandler.cs hérite de DelegatingHandler, qui à son tour hérite de HttpMessageHandler. Cela nous permet de remplacer la fonctionnalité permettant d'inspecter une requête HTTP et de déterminer si nous souhaitons autoriser cette requête à suivre le pipeline vers le prochain gestionnaire et contrôleur ou à arrêter la requête en envoyant une réponse personnalisée..

Dans cette classe, nous y parvenons en remplaçant la SendAsync méthode. Cette méthode recherche une clé API (CLÉ API) dans l'en-tête de chaque requête HTTP, et transmet la requête au contrôleur uniquement si une clé API valide est présente dans l'en-tête de la requête.

Maintenant, pour voir ce gestionnaire en action, nous devons d’abord l’enregistrer dans notre application dans le répertoire Application_Start méthode de la Global.asax fichier.

GlobalConfiguration.Configuration.MessageHandlers.Add (new APIKeyHandler ());

Essayez d’appeler n’importe quelle méthode que vous avez exposée via vos contrôleurs d’API Web et vous devriez voir «Bad API Key» en réponse..

Pour une démonstration dans cet article, j'utilise le même projet et les URL que j'ai créées dans mon précédent article, "Développement d'une API Web ASP.NET"..

Vérifions que le APIKeyHandlerfonctionne bien en créant une requête HTTP avec les en-têtes corrects. Pour cela, nous devons créer un en-tête HTTP avec une valeur de clé:

"API_KEY": "X-some-key"

J'utilise un plug-in de navigateur Mozilla appelé "Outil HTTP" pour créer des en-têtes de requête HTTP ici.

La requête HTTP est maintenant transmise au contrôleur par le gestionnaire.

Donc, notre gestionnaire de contrôle de clé d'API est en place maintenant. Cela sécurise notre API Web pour garantir que seuls les clients dotés de clés d'API valides peuvent accéder à ce service. Nous verrons ensuite comment mettre en œuvre une sécurité basée sur les rôles des utilisateurs..

Authentification de base

L'authentification de base, comme son nom l'indique, est la forme la plus simple et la plus simple d'authentification des requêtes HTTP. Le client envoie des informations d'identification codées en Base64 dans l'en-tête Autoriser à chaque demande HTTP, et ce n'est que si les informations d'identification sont vérifiées que l'API renvoie la réponse attendue. L'authentification de base ne nécessite ni stockage de session côté serveur ni implémentation de cookies car chaque demande est vérifiée par l'API..

Une fois que l’implémentation de l’authentification de base dans l’API Web est comprise, il sera très facile d’attacher d’autres formes d’authentification. Seul le processus d'authentification sera différent et les points d'ancrage de l'API Web seront les mêmes..

Pour vérifier les informations d'identification de l'utilisateur, nous créons un IPincipal objet qui représente le contexte de sécurité actuel.

Ajouter un nouveau dossier appelé Sécurité et une nouvelle classe TestAPIPrincipal.cs en cela.

Classe publique TestAPIPrincipal: IPrincipal // Constructeur public TestAPIPrincipal (string userName) UserName = userName; Identity = new GenericIdentity (userName);  chaîne publique UserName get; ensemble;  public IIdentity Identity get; ensemble;  public bool IsInRole (role string) if (role.Equals ("user")) return true;  else return false; 

le Identité objet associé au principal a une propriété appelée Est authentifié. Si l'utilisateur est authentifié, cette propriété retournera true; sinon, il retournera faux.

Maintenant, créons un autre gestionnaire appelé AuthHandler.cs.

Classe publique AuthHandler: DelegatingHandler string _userName = ""; // Méthode de validation des informations d'identification de l'autorisation // valeur de l'en-tête private bool ValidateCredentials (AuthenticationHeaderValue authenticationHeaderVal) try if (authenticationHeaderVal! = Null &&! String.IsNullOrEmpty (authenticationHeaderVal.Parameter)) string [] decoded (Convert.FromBase64String (authenticationHeaderVal.Parameter)) .Split (new [] ':'); // now decodedCredentials [0] contiendra // nom d'utilisateur et decodedCredentials [1] contiendra // mot de passe. if (decodedCredentials [0] .Equals ("nom d'utilisateur") && decodedCredentials [1] .Equals ("mot de passe")) _userName = "John Doe"; return true; // demande authentifiée.  return false; // demande non authentifiée.  catch return false;  protégé redéfinit la tâche asynchrone SendAsync (demande HttpRequestMessage, CancellationToken cancelToken) // si les informations d'identification sont validées, // définissez CurrentPrincipal et Current.User if (ValidateCredentials (request.Headers.Authorization)) Thread.CurrentPrincipal (new TestAPIP) (new TestAPIP). HttpContext.Current.User = new TestAPIPrincipal (_userName);  // Exécutez base.SendAsync pour exécuter les actions // par défaut. Une fois l'opération terminée, // capturez l'objet de réponse et ajoutez l'en-tête // WWW-Authenticate si la demande // a été marquée comme non autorisée. // Autorise la demande à traiter plus en aval dans le pipeline var response = wait base.SendAsync (request, cancelToken); if (response.StatusCode == HttpStatusCode.Unauthorized &&! response.Headers.Contains ("WwwAuthenticate")) response.Headers.Add ("WwwAuthenticate", "Basic");  retour réponse; 

Cette classe contient une méthode privée ValidateCredentials, qui vérifie les valeurs de nom d'utilisateur et de mot de passe décodées à partir de l'en-tête de la requête HTTP, ainsi que le SendAsyncméthode d'interception de la requête HTTP.

Si les informations d’identité du client sont valides, les informations actuelles IPincipalobjet est attaché au fil actuel, c.-à-d.. Thread.CurrentPrincipal. Nous avons également mis le HttpContext.Current.User rendre le contexte de sécurité cohérent. Cela nous permet d'accéder aux détails de l'utilisateur actuel de n'importe où dans l'application..

Une fois la demande authentifiée, base.SendAsync est appelé pour envoyer la demande au gestionnaire interne. Si la réponse contient un en-tête HTTP non autorisé, le code injecte une WwwAuthenticate en-tête avec la valeur De baseinformer le client que notre service attend une authentification de base.

Maintenant, nous devons enregistrer ce gestionnaire dans le répertoire Global.Asax classe comme nous l'avons fait pour notre ApiKeyHandler. Assurez-vous que le AuthHandlergestionnaire est en dessous du premier enregistrement de gestionnaire pour s'assurer du bon ordre.

GlobalConfiguration.Configuration.MessageHandlers.Add (new APIKeyHandler ()); GlobalConfiguration.Configuration.MessageHandlers.Add (new AuthHandler ());

Mais avant de pouvoir voir l'authentification de base en action, nous devrons d'abord implémenter l'autorisation.

Autorisation

L'autorisation vérifie si l'utilisateur authentifié peut effectuer une action particulière ou utiliser une ressource particulière. Ce processus dans l'API Web se produit plus tard dans le pipeline, après
authentification et avant l'exécution des actions du contrôleur.

Utilisation de l'attribut d'autorisation

L'API Web ASP.NET MVC fournit un filtre d'autorisation appelé AuthorizeAttributequi vérifie la demande IPincipal, vérifie sa Identity.IsAuthenticated propriété, et retourne un 401 non autorisé Statut HTTP si la valeur est false et que la méthode d'action demandée ne sera pas exécutée. Ce filtre peut être appliqué à différents niveaux, comme le niveau du contrôleur ou le niveau d’action, et s’applique facilement à l’aide de la touche [Autoriser]syntaxe sur les contrôleurs ou les actions.

[Autoriser] public class ClassifiedsController: ApiController

Une fois cet attribut défini, il empêchera l'accès à toutes les méthodes d'action du contrôleur par des utilisateurs non autorisés..

Tout d'abord, notre gestionnaire d'authentification de base intervient pour définir l'identité de l'utilisateur actuel. IPincipal objet. Puis, avant que cette demande n'atteigne le contrôleur, AuthorizeAttribute vérifie l'accès au contrôleur / à l'action en particulier pour l'utilisateur actuel.

Pour voir cela en action, créons d'abord une requête HTTP sans les informations d'identification appropriées.

L'accès est refusé par le AuthorizeAttribute.

Maintenant, créons une autre demande avec la clé / valeur d’en-tête d’autorisation cette fois-ci comme suit:

Autorisation: Base dXNlcm5hbWU6cGFzc3dvcmQ =

Ici, la valeur dXNlcm5hbWU6cGFzc3dvcmQ =est leForme codée en base64 de Identifiant Mot de passe.

Cette demande obtient les droits d'accès au contrôleur / à l'action comme prévu.

Ceci est un exemple de sécurisation des actions publiques du contrôleur entier.

Autorisation de niveau d'action

Nous pouvons également restreindre certaines parties des actions du contrôleur en définissant le paramètre[Autoriser] attribuer au niveau de l'action seulement à la place. Cela nous permettra d'avoir à la fois des actions protégées et des actions non protégées dans le même contrôleur..

// [Autoriser] public class ClassifiedsController: ApiController liste publique Get (id chaîne) return ClassifiedService.GetClassifieds (id);  [Autoriser] liste publique Get () return ClassifiedService.GetClassifieds (""); 

Attribut [AllowAnonymous]

Une autre façon d’avoir à la fois des actions protégées et des actions non protégées au sein du contrôleur consiste à utiliser le [AllowAnonymous] attribut. Quand on met le [Autoriser] attribuer au niveau du contrôleur et définir le [AllowAnonymous] attribut pour toute action à l’intérieur du contrôleur, cette action ignorera la [Autoriser] attribut.

Vérifications des rôles et des utilisateurs

Il est également possible de filtrer certains rôles et utilisateurs pour les droits d'accès. Par exemple, nous pouvons avoir quelque chose comme [Autoriser (Roles = "Admin")] sur les contrôleurs et les actions.

Attribut d'autorisation personnalisé

Enfin, nous pouvons également créer notre propre attribut d'autorisation personnalisé en fonction de nos besoins. L’un des moyens pour y parvenir consiste à étendre AuthorizeAttribute.

Supposons que nous voulions limiter notre service d'API Web à certaines parties du monde en limitant l'accès aux utilisateurs ne se trouvant pas dans une plage d'adresses IP donnée. Nous pouvons créer un attribut authorize personnalisé à cette fin en dérivant du AuthorizeAttributeclasse et dépassant la Est autorisé méthode.

Classe publique RestrictIPsAttribute: System.Web.Http.AuthorizeAttribute substitution protégée bool IsAuthorized (contexte HttpActionContext) var ip = HttpContext.Current.Request.UserHostAddress; // cherche ip ici si (ip.Contains ("")) return true;  return false; 

Une fois que nous avons notre attribut Authorize personnalisé, nous pouvons en décorer nos contrôleurs / actions.

[RestrictIPsAttribute] liste publique Get () return ClassifiedService.GetClassifieds (""); 

Conclusion

Dans cet article, nous avons examiné comment sécuriser notre service API Web ASP.NET avant de l'exposer au monde extérieur. Nous avons examiné comment nous pouvons authentifier les requêtes HTTP pour obtenir des clés d'API valides et des informations d'identification d'utilisateur valides. Avec autant de connaissances en main, je crois que nous sommes prêts à développer toute sécurité personnalisée pour nos API..

Pour ceux d'entre vous qui débutent avec Laravel ou qui souhaitent développer leurs connaissances, leur site ou leur application avec des extensions, nous pouvons étudier de nombreuses choses sur le marché Envato..

J'espère que vous avez apprécié la lecture autant que l'apprentissage de cet article, et n'oubliez pas de laisser des questions ou des commentaires dans le fil ci-dessous.!