Protezione dell'API Web ASP.NET

Dopo aver sviluppato la tua API Web, prima di esporla ai tuoi clienti, in base alle tue esigenze, potrebbe essere necessario proteggere alcune parti del servizio API in modo che solo gli utenti verificati possano accedere al servizio API. Questa protezione in ASP.NET può essere ottenuta utilizzando i meccanismi di autenticazione e autorizzazione.

Autenticazione

L'autenticazione è il processo per determinare se qualcuno o qualcosa è, in realtà, chi o cosa si pretende di essere. Utilizzando il meccanismo di autenticazione, ci assicuriamo che ogni richiesta ricevuta dal servizio API Web sia inviata da un cliente con credenziali adeguate.

Autenticazione tramite gestori di messaggi

UN gestore di messaggi è una classe che riceve una richiesta HTTP e restituisce una risposta HTTP. I gestori di messaggi sono classi derivate dalla classe astratta HttpMessageHandler. Sono utili per i problemi trasversali che operano a livello di messaggi HTTP (piuttosto che azioni del controllore). Ad esempio, un gestore di messaggi potrebbe:

  • leggere o modificare le intestazioni delle richieste
  • aggiungi un'intestazione di risposta alle risposte
  • convalidare le richieste prima che raggiungano il controller

In un'API Web, in genere, una serie di gestori di messaggi viene concatenata, formando un modello chiamato delegando gestore.

L'ordine in cui questi gestori sono impostati è importante in quanto verranno eseguiti in sequenza. 

Il gestore più importante si trova in cima, a guardia di tutto ciò che entra. Se i controlli passano, passerà questa richiesta lungo la catena al prossimo delegante, e così via. 

Se tutto va bene, arriverà al controller API ed eseguirà l'azione desiderata. Tuttavia, se uno dei controlli fallisce all'interno dei gestori, la richiesta viene respinta e viene inviata una risposta al client.

Con questa grande teoria in mano, ora scriviamo il codice per i nostri gestori. Creeremo due gestori di messaggi in questo articolo:

  1. APIKeyHandler: Il gestore responsabile dell'intercettazione di una richiesta HTTP e che assicura la sua intestazione contiene una chiave API
  2. AuthHandler: Gestore responsabile dell'autenticazione delle credenziali e dei ruoli di un utente

Autenticazione chiave API

Nel progetto API Web, creare una cartella chiamata MessageHandlerse aggiungi una classe APIKeyHandler.cs.

public class APIKeyHandler: DelegatingHandler // imposta una chiave API predefinita stringa const privata yourApiKey = "X-some-key"; operazione asincrona di protezione protetta SendAsync (richiesta HttpRequestMessage, CancellationToken cancellationToken) bool isValidAPIKey = false; IEnumerable lsHeaders; // Convalida dell'esistenza della chiave API var checkApiKeyExists = request.Headers.TryGetValues ​​("API_KEY", out lsHeaders); if (checkApiKeyExists) if (lsHeaders.FirstOrDefault (). Equals (yourApiKey)) isValidAPIKey = true;  // Se la chiave non è valida, restituire un codice di stato http. if (! isValidAPIKey) return request.CreateResponse (HttpStatusCode.Forbidden, "Bad API Key"); // Consenti alla richiesta di elaborare ulteriormente la pipeline var response = attendi base.SendAsync (request, cancellationToken); // Restituisce la risposta facendo il backup della risposta di ritorno della catena; 

Il APIKeyHandler.cs eredita da DelegatingHandler, da cui a sua volta eredita HttpMessageHandler. Questo ci consente di ignorare la funzionalità per l'ispezione di una richiesta HTTP e controllare se vogliamo consentire a questa richiesta di scorrere lungo la pipeline al gestore e al controller successivo o interrompere la richiesta inviando una risposta personalizzata.

In questa classe, stiamo raggiungendo questo superando il SendAsync metodo. Questo metodo cerca una chiave API (api_key) nell'intestazione di ogni richiesta HTTP e passa la richiesta al controller solo se nell'intestazione della richiesta è presente una chiave API valida.

Ora, per vedere questo gestore in azione, dobbiamo prima registrarlo alla nostra applicazione in Application_Start metodo dal global.asax file.

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

Prova a chiamare qualsiasi metodo che hai esposto tramite i tuoi controller API Web e dovresti vedere "Bad API Key" come risposta.

Per una demo in questo articolo, sto utilizzando lo stesso progetto e gli URL che ho creato nel mio precedente articolo, "Sviluppo di un'API Web ASP.NET".

Proviamo a verificare che il APIKeyHandlersta funzionando bene creando una richiesta HTTP con intestazioni corrette. Per questo, abbiamo bisogno di creare un'intestazione HTTP con valore chiave:

"API_KEY": "X-some-key"

Sto usando un plugin per browser Mozilla chiamato "Strumento HTTP" per creare intestazioni di richieste HTTP qui.

La richiesta HTTP è ora passata al gestore dal gestore.

Quindi il nostro gestore di verifica chiavi API è ora disponibile. Ciò protegge la nostra API Web per garantire che solo quei client che sono dotati di chiavi API valide possano accedere a questo servizio. Di seguito vedremo come possiamo implementare la sicurezza in base ai ruoli degli utenti.

Autenticazione di base

L'autenticazione di base, come suggerisce il nome, è la forma più semplice e basilare di autenticazione delle richieste HTTP. Il client invia le credenziali codificate Base64 nell'intestazione Autorizza su ogni richiesta HTTP e solo se le credenziali vengono verificate l'API restituisce la risposta prevista. L'autenticazione di base non richiede l'archiviazione della sessione lato server o l'implementazione dei cookie poiché ogni richiesta viene verificata dall'API.

Una volta compresa l'implementazione dell'autenticazione di base nell'API Web, sarà molto facile agganciare altre forme di autenticazione. Solo il processo di autenticazione sarà diverso e l'hook Web API, dove viene eseguito, sarà lo stesso.

Per verificare le credenziali dell'utente, creiamo un IPrincipal oggetto che rappresenta l'attuale contesto di sicurezza.

Aggiungi una nuova cartella chiamata Sicurezza e una nuova classe TestAPIPrincipal.cs dentro.

public class TestAPIPrincipal: IPrincipal // Constructor public TestAPIPrincipal (string userName) UserName = userName; Identity = new GenericIdentity (userName);  public string UserName get; impostato;  public IIdentity Identity get; impostato;  public bool IsInRole (ruolo stringa) if (role.Equals ("utente")) return true;  else return false; 

Il IIdentity l'oggetto associato al principale ha una proprietà chiamata IsAuthenticated. Se l'utente è autenticato, questa proprietà restituirà true; altrimenti, restituirà false.

Ora, creiamo un altro gestore chiamato AuthHandler.cs.

public class AuthHandler: DelegatingHandler string _userName = ""; // Metodo per convalidare le credenziali dal valore di intestazione // dell'autorizzazione bool privato ValidateCredentials (AuthenticationHeaderValue authenticationHeaderVal) try if (authenticationHeaderVal! = Null &&! String.IsNullOrEmpty (authenticationHeaderVal.Parameter)) string [] decodedCredentials = Encoding.ASCII.GetString (Convert.FromBase64String (authenticationHeaderVal.Parameter)) .Split (new [] ':'); // now decodedCredentials [0] conterrà // username e decodedCredentials [1] // contiene la password. if (decodedCredentials [0] .Equals ("username") && decodedCredentials [1] .Equals ("password")) _userName = "John Doe"; return true; // request authenticated.  return false; // request not authenticated.  catch return false;  Attività di asincronia protetta sovrascritta SendAsync (richiesta HttpRequestMessage, CancellationToken cancellationToken) // se le credenziali sono convalidate, // imposta CurrentPrincipal e Current.User if (ValidateCredentials (request.Headers.Authorization)) Thread.CurrentPrincipal = new TestAPIPrincipal (_userName); HttpContext.Current.User = new TestAPIPrincipal (_userName);  // Esegui base.SendAsync per eseguire azioni // predefinite e, una volta completato, // acquisisce l'oggetto risposta e aggiunge // Intestazione WWW-Authenticate se la richiesta // è stata contrassegnata come non autorizzata. // Consenti alla richiesta di elaborare ulteriormente la pipeline var response = attendi base.SendAsync (request, cancellationToken); if (response.StatusCode == HttpStatusCode.Unauthorized &&! response.Headers.Contains ("WwwAuthenticate")) response.Headers.Add ("WwwAuthenticate", "Basic");  risposta di risposta; 

Questa classe contiene un metodo privato ValidateCredentials, che controlla i valori decodificati di nome utente e password dall'intestazione della richiesta HTTP, e anche il SendAsyncmetodo per intercettare la richiesta HTTP.

Se le credenziali del client sono valide, allora quella corrente IPrincipall'oggetto è collegato al thread corrente, ad es. Thread.CurrentPrincipal. Abbiamo anche impostato il HttpContext.Current.User per rendere coerente il contesto di sicurezza. Questo ci consente di accedere ai dettagli dell'utente corrente da qualsiasi punto dell'applicazione.

Una volta che la richiesta è autenticata, base.SendAsync viene chiamato per inviare la richiesta al gestore interno. Se la risposta contiene un'intestazione non autorizzata HTTP, il codice inietta a WwwAuthenticate intestazione con il valore Di baseper informare il cliente che il nostro servizio si aspetta l'autenticazione di base.

Ora, dobbiamo registrare questo gestore nel Global.asax classe come abbiamo fatto per il nostro ApiKeyHandler. Assicurarsi che il AuthHandleril gestore è al di sotto della prima registrazione dell'handler per assicurarsi del giusto ordine.

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

Ma prima di poter vedere l'autenticazione di base in azione, dovremo prima implementare l'autorizzazione.

Autorizzazione

L'autorizzazione sta verificando se l'utente autenticato può eseguire una determinata azione o consumare una particolare risorsa. Questo processo in Web API avviene più tardi nella pipeline, dopo
autenticazione e prima che vengano eseguite le azioni del controllore.

Utilizzando Autorizza attributo

L'API Web MVC di ASP.NET fornisce un filtro di autorizzazione chiamato AuthorizeAttributeche verifica la richiesta IPrincipal, controlla il suo Identity.IsAuthenticated proprietà e restituisce a 401 non autorizzato Stato HTTP se il valore è falso e il metodo di azione richiesto non verrà eseguito. Questo filtro può essere applicato a diversi livelli, come il livello del controller o il livello di azione, e può essere facilmente applicato utilizzando il [Autorizzare]sintassi su controller o azioni.

[Autorizza] Classe pubblica ClassifiedsController: ApiController

Una volta impostato questo attributo, impedirà l'accesso a tutti i metodi di azione nel controllore da parte di utenti non autorizzati.

Per prima cosa, il nostro gestore di autenticazione di base si attiva per impostare l'identità dell'utente corrente IPrincipal oggetto. Quindi, prima che questa richiesta raggiunga il controller, AuthorizeAttribute verifica l'accesso al particolare controller / azione per l'utente corrente.

Per vedere questo in azione, prima creiamo una richiesta HTTP senza credenziali adeguate.

L'accesso viene negato dal AuthorizeAttribute.

Ora, creiamo un'altra richiesta con chiave / valore dell'intestazione Autorizzazione questa volta come segue:

Autorizzazione: Basic dXNlcm5hbWU6cGFzc3dvcmQ =

Qui, il valore dXNlcm5hbWU6cGFzc3dvcmQ =è ilForma codificata Base64 di username: password.

Questa richiesta ottiene i diritti di accesso al controller / all'azione come previsto.

Questo è un esempio di protezione delle azioni pubbliche dell'intero controllore.

Autorizzazione a livello di azione

Possiamo anche limitare alcune parti delle azioni del controller impostando il[Autorizzare] attributo solo al livello di azione. Ciò consentirà di avere azioni protette e non protette nello stesso controller.

// [Autorizza] public class ClassifiedsController: ApiController elenco pubblico Get (stringa id) return ClassifiedService.GetClassifieds (id);  [Autorizza] Elenco pubblico Get () return ClassifiedService.GetClassifieds (""); 

[AllowAnonymous] Attributo

Un altro modo per avere azioni sia protette che non protette all'interno del controller è facendo uso di [AllowAnonymous] attributo. Quando impostiamo il [Autorizzare] attributo nel livello del controller e impostare il [AllowAnonymous] attributo per qualsiasi azione all'interno del controller, quell'azione salterà il [Autorizzare] attributo.

Ruolo e controlli degli utenti

È anche possibile filtrare determinati ruoli e utenti per i diritti di accesso. Ad esempio, possiamo avere qualcosa di simile [Autorizza (ruoli = "amministratore")] sui controller e le azioni.

Attributo di autorizzazione personalizzata

Infine, possiamo anche creare il nostro attributo di autorizzazione personalizzato in base alle nostre esigenze. Uno dei modi per raggiungere questo obiettivo è l'estensione AuthorizeAttribute.

Diciamo che vogliamo limitare il nostro servizio API Web solo a determinate parti del mondo limitando l'accesso agli utenti che non si trovano entro un certo intervallo di indirizzi IP. Possiamo creare un attributo di autorizzazione personalizzato per questo scopo derivando da AuthorizeAttributeclasse e override del IsAuthorized metodo.

public class RestrictIPsAttribute: System.Web.Http.AuthorizeAttribute protected override bool IsAuthorized (contesto HttpActionContext) var ip = HttpContext.Current.Request.UserHostAddress; // controlla ip qui se (ip.Contains ("")) return true;  return false; 

Una volta ottenuto il nostro attributo Authorize personalizzato, possiamo decorare i nostri controller / azioni con esso.

[RestrictIPsAttribute] Elenco pubblico Get () return ClassifiedService.GetClassifieds (""); 

Conclusione

In questo articolo, abbiamo esaminato come possiamo proteggere il nostro servizio API Web ASP.NET prima di esporre il servizio al mondo esterno. Abbiamo esaminato in che modo è possibile autenticare le richieste HTTP per chiavi API valide e credenziali utente valide. Con questa grande conoscenza in mano, credo che siamo pronti a sviluppare qualsiasi sicurezza personalizzata per le nostre API.

Per quelli di voi che stanno appena iniziando con Laravel o stanno cercando di espandere le vostre conoscenze, il vostro sito o la vostra applicazione con estensioni, abbiamo una varietà di cose che potete studiare nel mercato Envato.

Spero ti sia piaciuto leggere tanto quanto imparare da questo articolo e ricorda di lasciare qualsiasi domanda o commento nel feed qui sotto!