Freigeben über


ASP.NET

Sicherheitsfilter von ASP.NET-Web-API

Badrinarayanan Lakshmiraghavan

Authentifizierung und Autorisierung sind die Eckpfeiler der Anwendungssicherheit. Die Authentifizierung weist die Identität eines Benutzers nach, indem die Anmeldeinformationen überprüft werden. Demgegenüber ermittelt die Autorisierung, ob ein Benutzer zur Ausführung einer angeforderten Aktion berechtigt ist. Eine sichere Web-API authentifiziert Anforderungen und autorisiert den Zugriff auf die angeforderte Ressource auf der Grundlage der nachgewiesenen Identität.

Sie können die Authentifizierung in einer ASP.NET-Web-API mithilfe der Erweiterungspunkte in der ASP.NET-Web-API-Pipeline sowie über die vom Host bereitgestellten Optionen implementieren. Bei der ersten Version der ASP.NET-Web-API ist es allgemein üblich, zur Implementierung der Authentifizierung einen Autorisierungs- oder Aktionsfilter zu verwenden. Mit ASP.NET-Web-API 2 wird ein neuer Authentifizierungsfilter für den Prozess eingeführt. Dieser neue Erweiterungspunkt ermöglicht die saubere Trennung von Authentifizierungs- und Autorisierungsaspekten. In diesem Artikel stelle ich diese beiden Sicherheitsfilter vor und demonstriere, wie Sie die Authentifizierung und Autorisierung als separate Aspekte in der ASP.NET-Web-API implementieren.

Optionen für die Implementierung von Sicherheitsaspekten

Authentifizierung und Autorisierung in der ASP.NET-Web-API lassen sich mithilfe der Erweiterungspunkte, die vom Host bzw. der ASP.NET-Web-API-Pipeline selbst bereitgestellt werden, implementieren. Die hostbasierten Optionen umfassen HTTP-Module und OWIN-Middlewarekomponenten, während die ASP.NET-Web-API-Erweiterungsoptionen Meldungshandler sowie Aktions-, Autorisierungs- und Authentifizierungsfilter enthalten.

Hostbasierte Optionen fügen sich nahtlos in die Hostpipeline ein und sind in der Lage, ungültige Anforderungen weiter oben in der Pipeline abzulehnen. Dagegen bieten die ASP.NET-Web-API-Erweiterungsoptionen differenziertere Steuerungsmöglichkeiten über den Authentifizierungsprozess. Auf diese Weise können Sie verschiedene Authentifizierungsmechanismen für unterschiedliche Controller und sogar unterschiedliche Aktionsmethoden festlegen. Es gilt also, Folgendes abzuwägen: bessere Integration mit dem Host und frühe Ablehnung ungültiger Anforderungen oder differenzierte Steuerung der Authentifizierung. Neben diesen allgemeinen Merkmalen verfügt jede Option zudem über Vor- und Nachteile, die ich in den folgenden Abschnitten erörtern werde.

HTTP-Modul Eine Option für Web-APIs, die unter IIS ausgeführt werden. Mithilfe von HTTP-Modulen kann Sicherheitscode zu einem frühen Zeitpunkt in der IIS-Pipeline ausgeführt werden. Der von einem HTTP-Modul nachgewiesene Prinzipal ist für alle Komponenten verfügbar, einschließlich der IIS-Komponenten, die später in der Pipeline ausgeführt werden. Wird der Prinzipal von einem HTTP-Modul z. B. als Antwort auf das AuthenticateRequest-Ereignis nachgewiesen, wird der Benutzername des Prinzipals ordnungsgemäß im Feld "cs-username" der IIS-Protokolle protokolliert. Der größte Nachteil bei HTTP-Modulen ist das Fehlen von Granularität. HTTP-Module werden für alle Anforderungen ausgeführt, die die Anwendung erreichen. Bei einer Webanwendung mit unterschiedlichen Funktionen – wie HTML-Markupgenerierung, Web-APIs usw. – ist ein HTTP-Modul, das die Authentifizierung in eine Richtung durchsetzt, in der Regel keine hinreichend flexible Lösung. Ein weiterer Nachteil bei der Verwendung eines HTTP-Moduls ist die Abhängigkeit vom Host – in diesem Fall also IIS.

OWIN-Middleware Eine weitere hostbezogene Option, die für OWIN-Hosts verfügbar ist. OWIN wird von ASP.NET-Web-API 2 vollständig unterstützt. Der vielleicht wichtigste Grund für die Verwendung von OWIN-Middleware als Sicherheitsoption liegt darin, dass dieselbe Middleware Framework-übergreifend eingesetzt werden kann. Das bedeutet, dass Sie mehrere Frameworks wie ASP.NET-Web-API, SignalR usw. für Ihre Anwendung nutzen und dabei dennoch auf gängige Sicherheitsmiddleware zurückgreifen können. Die minimale Granularität der OWIN-Middleware könnte jedoch ein Nachteil sein, weil die OWIN-Middleware in der OWIN-Pipeline ausgeführt und normalerweise für alle Anforderungen aufgerufen wird. Auch wenn die OWIN-Middleware nur mit OWIN-kompatiblen Hosts verwendet werden kann, ist diese Abhängigkeit im Vergleich besser als die Abhängigkeit von einem spezifischen Host/Server wie IIS, wie dies bei HTTP-Modulen der Fall ist. An dieser Stelle sei anzumerken, dass OWIN-Middleware dank des Pakets "Microsoft.Owin.Host.SystemWeb" in der (in IIS integrierten) ASP.NET-Pipeline ausgeführt werden kann.

Meldungshandler Eine von der ASP.NET-Web-API bereitgestellte Erweiterungsoption, deren größter Vorteil als Sicherheitsoption darin liegt, dass der Meldungshandler ein Konzept des ASP.NET-Web-API-Frameworks ist und folglich nicht vom zugrunde liegenden Host oder Server abhängt. Darüber hinaus wird ein Meldungshandler nur für Web-API-Anforderungen ausgeführt. Der Nachteil der Verwendung eines Meldungshandlers liegt im Fehlen differenzierter Steuerungsmöglichkeiten. Ein Meldungshandler kann so konfiguriert werden, dass er als globaler Handler für alle Anforderungen oder für eine bestimmte Route ausgeführt wird. Einer bestimmten Route können mehrere Controller zugeordnet sein. Alle diese Controller und die Aktionsmethoden, die darin enthalten sind, müssen dieselbe Authentifizierung verwenden, die von dem für diese Route konfigurierten Meldungshandler durchgesetzt wird. Anders ausgedrückt, weist die von einem Meldungshandler implementierte Authentifizierung die niedrigste Granularität auf Routenebene auf.

Aktionsfilter Eine weitere, von der ASP.NET-Web-API bereitgestellte Erweiterungsoption ist der Aktionsfilter. Vom Standpunkt einer Authentifizierungslösung ist dies keine brauchbare Option, weil der Aktionsfilter erst ausgeführt wird, nachdem die Authentifizierungsfilter in der ASP.NET-Web-API-Pipeline ausgeführt wurden. Damit Authentifizierung und Autorisierung ordnungsgemäß funktionieren, muss die Authentifizierung vor der Autorisierung erfolgen.

Autorisierungsfilter Eine weitere, von der ASP.NET-Web-API bereitgestellte Erweiterungsoption ist der Autorisierungsfilter. Eine der häufigsten Methoden zur Implementierung der benutzerdefinierten Authentifizierung für Szenarien, die eine höhere Granularität als die der Meldungshandler erfordern, ist die Verwendung eines Autorisierungsfilters. Das Hauptproblem bei der Verwendung eines Autorisierungsfilters sowohl für die Authentifizierung als auch für die Autorisierung besteht darin, dass die Ausführungsreihenfolge der Autorisierungsfilter von der ASP.NET-Web-API nicht zugesichert wird. Es kann also leicht passieren, dass ein Autorisierungsfilter, der für die Autorisierung verantwortlich ist, vor dem Autorisierungsfilter ausgeführt wird, der für die Authentifizierung verantwortlich ist. Dadurch wären die Autorisierungsfilter genauso unbrauchbar für die Authentifizierung wie die Aktionsfilter.

Authentifizierungsfilter Der Schwerpunkt dieses Artikels liegt auf der neuesten Erweiterungsoption für ASP.NET-Web-API 2: Authentifizierungsfilter, die nach den Meldungshandlern, aber vor allen übrigen Filtertypen ausgeführt werden. Aus diesem Grund sind sie die bessere Wahl für die Implementierung von Authentifizierungsaspekten. Vor allem aber werden Authentifizierungsfilter vor Autorisierungsfiltern ausgeführt. Durch die Verwendung eines Filters, der auf die Authentifizierung oder Autorisierung ausgerichtet ist, können Sie Authentifizierungs- und Autorisierungsaspekte trennen.

Darüber hinaus zeichnen sich Authentifizierungsfilter durch Steuerungsmöglichkeiten bzw. eine Granularität aus, die sie besonders nützlich macht. Nehmen Sie als Beispiel eine Web-API, die für die Nutzung sowohl durch systemeigene mobile Apps als auch durch browserbasierte AJAX-Anwendungen konzipiert ist. Die mobile App verfügt möglicherweise über ein Token im HTTP-Autorisierungsheader, während die AJAX-App ggf. ein Authentifizierungscookie als Anmeldeinformation verwendet. Stellen Sie sich weiter vor, dass ein Teilbereich der API vertraulich und nur für die systemeigene mobile App verfügbar ist und Sie sicherstellen möchten, dass der Zugriff auf die Aktionsmethoden nur über ein Token und nicht über ein Cookie zulässig sein soll (im Unterschied zu einem Token in einem HTTP-Autorisierungsheader sind Cookies anfällig für websiteübergreifende Anforderungsfälschungen (Cross-Site Request Forgery, XSRF)). In diesem Fall muss die Authentifizierung einer höheren Granularität unterliegen, als sie bei einer hostbasierten Option bzw. sogar einem Meldungshandler möglich ist. Ein Authentifizierungsfilter erfüllt perfekt die Anforderungen dieses Anwendungsfalls. Sie können den Authentifizierungsfilter tokenbasiert auf alle diese Controller oder Aktionsmethoden anwenden, die dies voraussetzen, und die Authentifizierungsfilter cookiebasiert an anderen Orten verwenden. Nehmen wir an, Sie verfügen in diesem Szenario über einige allgemeine Aktionsmethoden, die sowohl über Tokens als auch über Cookies zugänglich sein sollen. Sie können einfach sowohl die cookie- als auch die tokenbasierten Authentifizierungsfilter auf diese allgemeinen Aktionsmethoden anwenden und sicher sein, dass einer der Filter eine erfolgreiche Authentifizierung ergeben wird. Dieses Steuerungsverhalten ist der größte Vorteil, der die Authentifizierungsfilter auszeichnet. Wenn eine differenziertere Steuerung der Authentifizierung erforderlich ist, besteht der richtige Ansatz darin, Authentifizierungsaspekte über einen Authentifizierungsfilter und Autorisierungsaspekte über einen Autorisierungsfilter zu implementieren.

An dieser Stelle sollte erwähnt werden, dass der sofort einsatzbereite Authentifizierungsfilter "HostAuthenticationFilter" die ASP.NET-Web-API-Authentifizierung über OWIN-Middleware möglich macht. Obwohl die OWIN-Authentifizierungsmiddleware in einer Pipeline ausgeführt wird und versucht, die eingehenden Anforderungen "aktiv" zu authentifizieren, kann sie auch so konfiguriert werden, dass sie die Anforderung nach entsprechender Aufforderung "passiv" authentifiziert. Bei Verwendung von "HostAuthenticationFilter" kann die passive OWIN-Authentifizierungsmiddleware später in der Web-API-Pipeline auch namensbasiert ausgeführt werden. Dies ermöglicht die Verwendung eines Authentifizierungscodes, der Framework-übergreifend (einschließlich der von Microsoft bereitgestellten OWIN-Authentifizierungsmiddleware) verwendet werden kann, während die aktionsbezogene Granularität für die Authentifizierung weiterhin unterstützt wird.

Obwohl Sie die Authentifizierung auf Hostebene mit der präziseren Authentifizierung auf Grundlage der Web-API-Pipeline kombinieren können, sollten Sie sorgfältig prüfen, wie sich die Authentifizierung auf Hostebene auf die Web-API-Authentifizierung auswirken kann. Beispielsweise könnten Sie cookiebasierte Authentifizierungsmiddleware auf der Hostebene implementieren, um sie für andere Frameworks wie ASP.NET MVC zu verwenden. Dabei ist jedoch zu beachten, dass die Web-API anfälliger für Sicherheitsangriffe wie XSRF ist, wenn sie den cookiebasierten Prinzipal verwendet. In diesen Situationen hilft die Erweiterungsmethode "SuppressDefaultHostAuthentication", die der Web-API die Möglichkeit gibt, die auf der Hostebene konfigurierten Authentifizierungen zu ignorieren. Die Visual Studio-Standardvorlage für die Web-API verfügt über Cookies, die auf Hostebene aktiviert sind, und setzt Trägertokens auf der Web-API-Ebene ein. Da Cookies auf Hostebene aktiviert werden und Maßnahmen gegen XSRF erfordern, verwendet die Vorlage zusätzlich "SuppressDefaultHostAuthentication", um die Verwendung des cookiebasierten Prinzipals durch die Web-API-Pipeline zu verhindern. Dadurch verwendet die Web-API nur den tokenbasierten Prinzipal, und Sie müssen keinen Mechanismus auf der Web-API-Ebene vorsehen, der vor XSRF-Angriffen schützt.

Parallele Verwendung von Authentifizierungs- und Autorisierungsfiltern

In der ASP.NET-Web-API-Pipeline werden erst Authentifizierungsfilter und dann die Autorisierungsfilter ausgeführt. Das hat den einfachen Grund, dass die Autorisierung von der nachgewiesenen Identität abhängt, die als Ergebnis der Authentifizierung zurückgegeben wird. Hier erfahren Sie, wie Sie die Authentifizierungs- und Autorisierungsfilter so einrichten, dass sie zusammen die Sicherheit der ASP.NET-Web-API gewährleisten.

Das Grundprinzip dieser Vorgehensweise besteht darin, dem Authentifizierungsfilter die alleinige Verantwortung für die Überprüfung der Anmeldeinformationen einzuräumen, ohne dass er sich mit anderen Aspekten auseinandersetzen muss. Für den Fall, dass keine Anmeldeinformationen vorhanden sind, lehnt der Authentifizierungsfilter beispielsweise Anforderungen mit dem Statuscode "401 – Nicht autorisiert" nicht ab. Es wird einfach keine authentifizierte Identität nachgewiesen, und die Frage, wie anonyme Anforderungen zu behandeln sind, wird der Autorisierungsphase überlassen. Ein Authentifizierungsfilter weist drei grundlegende Aktionstypen auf:

  1. Wenn die betreffenden Anmeldeinformationen nicht in der Anforderung enthalten sind, führt der Filter keine Aktion aus.
  2. Wenn die Anmeldeinformationen vorhanden sind und für gültig befunden werden, weist der Filter die Identität in Form eines authentifizierten Prinzipals nach.
  3. Wenn die Anmeldeinformationen vorhanden sind, aber für ungültig befunden werden, benachrichtigt der Filter das ASP.NET-Web-API-Framework, indem er ein Fehlerergebnis festlegt. Dieses führt grundsätzlich zur Antwort "Nicht autorisiert", die an den Anforderer zurückgesendet wird.

Wenn keiner der Authentifizierungsfilter, die in der Pipeline ausgeführt werden, ungültige Anmeldeinformationen erkennen kann, wird die Pipeline weiter ausgeführt, auch wenn noch keine authentifizierte Identität nachgewiesen wurde. Die Behandlung dieser anonymen Anforderung wird einfach den Komponenten überlassen, die später in der Pipeline ausgeführt werden.

Auf der grundlegendsten Ebene überprüft ein Autorisierungsfilter einfach, ob es sich bei der nachgewiesenen Identität um eine authentifizierte Identität handelt. Durch einen Autorisierungsfilter kann aber auch sichergestellt werden, dass

  • sich der Benutzername der authentifizierten Identität auf der Liste der zulässigen Benutzer befindet.
  • sich mindestens eine der Rollen, die der authentifizierten Identität zugeordnet sind, auf der Liste der zulässigen Rollen befindet.

Während der direkt einsetzbare Autorisierungsfilter nur die soeben beschriebene rollenbasierte Zugriffssteuerung ausführt, kann ein benutzerdefinierter Autorisierungsfilter, der vom direkt einsetzbaren Autorisierungsfilter abgeleitet wird, eine anspruchsbasierte Zugriffssteuerung ausführen. Dazu werden die Ansprüche geprüft, die Teil der vom Authentifizierungsfilter nachgewiesenen Identität sind.

Wenn die Bedingungen aller Autorisierungsfilter erfüllt sind, wird die Pipeline weiter ausgeführt, und die Aktionsmethode des API-Controllers generiert schließlich eine Antwort auf die Anforderung. Wenn die Identität nicht nachgewiesen wird oder ein Konflikt in Bezug auf den Benutzernamen oder die Rollenanforderungen vorliegt, lehnt der Autorisierungsfilter die Anforderung mit der Antwort "401 – Nicht autorisiert" ab. Abbildung 1 veranschaulicht die Rolle der beiden Filter in den drei folgenden Szenarien: (1) Anmeldeinformationen nicht vorhanden, (2) ungültige bzw. (3) gültige Anmeldeinformationen vorhanden.

Security Filters in ASP.NET Web API Pipeline
Abbildung 1 – Sicherheitsfilter in der ASP.NET-Web-API-Pipeline

Erstellen eines Authentifizierungsfilters

Ein Authentifizierungsfilter ist eine Klasse, die die IAuthenticationFilter-Schnittstelle implementiert. Diese Schnittstelle verfügt über zwei Methoden: "AuthenticateAsync" und "ChallengeAsync", wie im Folgenden dargestellt:

public interface IAuthenticationFilter : IFilter
{
  Task AuthenticateAsync(HttpAuthenticationContext context, 
    CancellationToken cancellationToken);
  Task ChallengeAsync(HttpAuthenticationChallengeContext context,
    CancellationToken cancellationToken);
}

Die AuthenticateAsync-Methode akzeptiert "HttpAuthentication­Context" als Argument. Durch diesen Kontext wird festgelegt, wie die AuthenticateAsync-Methode das Ergebnis des Authentifizierungsprozesses zurück an das ASP.NET-Web-API-Framework kommuniziert. Wenn die Anforderungsmeldung authentische Anmeldeinformationen enthält, wird die Principal-Eigenschaft des übergebenen HttpAuthenticationContext-Objekts auf den authentifizierten Prinzipal festgelegt. Sind die Anmeldeinformationen ungültig, wird die ErrorResult-Eigenschaft des HttpAuthenticationContext-Parameters auf "UnauthorizedResult" festgelegt. Enthält die Anforderungsmeldung überhaupt keine Anmeldeinformationen, führt die AuthenticateAsync-Methode keine Aktion aus. Der Code in Abbildung 2 zeigt eine typische Implementierung der AuthenticateAsync-Methode für die drei Szenarien. Der in diesem Beispiel verwendete authentifizierte Prinzipal ist ein "ClaimsPrincipal" nur mit Namens- und Rollenansprüchen.

Abbildung 2 – die Methode "AuthenticateAsync"

public Task AuthenticateAsync(HttpAuthenticationContext context,
  CancellationToken cancellationToken)
{
  var req = context.Request;
  // Get credential from the Authorization header 
  //(if present) and authenticate
  if (req.Headers.Authorization != null &&
    "somescheme".Equals(req.Headers.Authorization.Scheme,
      StringComparison.OrdinalIgnoreCase))
  {
    var creds = req.Headers.Authorization.Parameter;
    if(creds == "opensesame") // Replace with a real check
    {
      var claims = new List<Claim>()
      {
        new Claim(ClaimTypes.Name, "badri"),
        new Claim(ClaimTypes.Role, "admin")
      };
      var id = new ClaimsIdentity(claims, "Token");
      var principal = new ClaimsPrincipal(new[] { id });
      // The request message contains valid credential
      context.Principal = principal;
    }
    else
    {
      // The request message contains invalid credential
      context.ErrorResult = new UnauthorizedResult(
        new AuthenticationHeaderValue[0], context.Request);
    }
  }
  return Task.FromResult(0);
}

Sie verwenden die AuthenticateAsync-Methode zum Implementieren der zentralen Authentifizierungslogik, die die Anmeldeinformationen in der Anforderung überprüft, und die ChallengeAsync-Methode zum Hinzufügen der Authentifizierungsaufforderung. Eine Authentifizierungsaufforderung wird einer Antwort hinzugefügt, wenn der Statuscode "401 – Nicht autorisiert“ lautet. Zur Untersuchung des Statuscodes benötigen Sie das Antwortobjekt. Mit der ChallengeAsync-Methode können Sie die Antwort jedoch nicht direkt untersuchen bzw. die Aufforderung nicht direkt festlegen. Diese Methode wird in der Web-API-Pipeline im Abschnitt zur Verarbeitung von Anforderungen tatsächlich vor der Aktionsmethode ausgeführt. Durch den Parameter der ChallengeAsync-Methode "HttpAuthenticationChallengeContext" kann der Result-Eigenschaft jedoch ein Aktionsergebnisobjekt (IHttpActionResult) zugeordnet werden. Die ExecuteAsync-Methode des Aktionsergebnisobjekts wartet, bis der Task die Antwort generiert, untersucht den Statuscode der Antwort und fügt den Antwortheader "WWW-Authenticate" hinzu. Der Code in Abbildung 3 zeigt eine typische Implementierung der ChallengeAsync-Methode. In diesem Beispiel habe ich nur eine hartcodierte Aufforderung hinzugefügt. Die ResultWithChallenge-Klasse entspricht der Aktionsergebnisklasse, die ich zum Hinzufügen der Aufforderung erstellt habe.

Abbildung 3 – die Methode "ChallengeAsync"

public Task ChallengeAsync(HttpAuthenticationChallengeContext context,
  CancellationToken cancellationToken)
{
  context.Result = new ResultWithChallenge(context.Result);
  return Task.FromResult(0);
}
public class ResultWithChallenge : IHttpActionResult
{
  private readonly IHttpActionResult next;
  public ResultWithChallenge(IHttpActionResult next)
  {
    this.next = next;
  }
  public async Task<HttpResponseMessage> ExecuteAsync(
    CancellationToken cancellationToken)
  {
    var response = await next.ExecuteAsync(cancellationToken);
    if (response.StatusCode == HttpStatusCode.Unauthorized)
    {
      response.Headers.WwwAuthenticate.Add(
        new AuthenticationHeaderValue("somescheme", "somechallenge"));
    }
    return response;
  }
}

Der folgende Code zeigt die fertige Filterklasse:

public class TokenAuthenticationAttribute : 
  Attribute, IAuthenticationFilter
{
  public bool AllowMultiple { get { return false; } }
  // The AuthenticateAsync and ChallengeAsync methods go here
}

Zusätzlich zur Implementierung der IAuthenticationFilter-Schnittstelle führe ich eine Ableitung von "Attribute" aus, damit diese Klasse auf ein Attribut auf Klassen- (Controller-) oder Methoden- (Aktions-)Ebene angewendet werden kann.

Folglich können Sie einen Authentifizierungsfilter erstellen, der nur für die Authentifizierung spezifischer Anmeldeinformationen (in diesem Beispiel ein fiktives Token) verantwortlich ist. Ein Authentifizierungsfilter verfügt über keine Autorisierungslogik, sein einziger Zweck ist die Authentifizierungsverarbeitung: Nachweisen einer Identität (beim Verarbeiten der Anforderungsmeldung) und Zurückgeben einer Aufforderung (beim Verarbeiten der Antwortmeldung) – natürlich nur, wenn diese vorhanden sind. Die Autorisierungsaspekte werden von Autorisierungsfiltern behandelt. Beispielsweise prüfen sie, ob die Identität einer authentifizierten Identität entspricht bzw. ob sie auf der Liste zulässiger Benutzer oder Rollen steht.

Verwenden eines Autorisierungsfilters

Das grundlegende Ziel der Verwendung eines Autorisierungsfilters ist die Autorisierung, um zu ermitteln, ob ein Benutzer Zugriff auf eine angeforderte Ressource erhalten soll. Die Web-API stellt eine Implementierung eines Autorisierungsfilters mit dem Namen "AuthorizeAttribute" bereit. Durch die Anwendung dieses Filters wird sichergestellt, dass es sich bei der Identität um eine authentifizierte Identität handelt. Sie können das Authorize-Attribut auch mit einer Liste spezifischer Benutzernamen und zulässiger Rollen konfigurieren. Der Code in Abbildung 4 zeigt den Autorisierungsfilter, der mithilfe der verschiedenen Attribute der zu autorisierenden Identität auf verschiedenen Ebenen (global, auf Controllerebene und auf Ebene der Aktionsmethode) angewendet wurde. Durch den Filter in diesem Beispiel wird global sichergestellt, dass die Identität authentifiziert wird. Durch den auf der Controllerebene verwendeten Filter wird sichergestellt, dass die Identität authentifiziert wird und dass mindestens eine Rolle, die der Identität zugeordnet ist, "admin" ist. Durch den auf der Ebene der Aktionsmethode verwendeten Filter wird sichergestellt, dass die Identität authentifiziert wird und der Benutzername "badri" lautet. An dieser Stelle möchte ich anmerken, dass der Autorisierungsfilter auf der Ebene der Aktionsmethode auch die Filter der globalen und Controllerebene erbt. Damit die Autorisierung erfolgreich ist, müssen deshalb alle Filter die folgenden Bedingungen erfüllen: Der Benutzername muss "badri" und eine der Rollen muss "admin" sein, und der Benutzer muss authentifiziert werden.

Abbildung 4 – Verwenden eines Autorisierungsfilters auf drei unterschiedlichen Ebenen

public static class WebApiConfig
{
  public static void Register(HttpConfiguration config)
  {
    // Other Web API configuration code goes here
    config.Filters.Add(new AuthorizeAttribute()); // Global level
  }
}
[Authorize(Roles="admin")] // Controller level
public class EmployeesController : ApiController
{
  [Authorize(Users="badri")] // Action method level
  public string Get(int id)
  {
    return “Hello World”;
  }
}

Obwohl das direkt einsetzbare "AuthorizeAttribute" bereits sehr nützlich ist, können Sie es unterklassifizieren, um zusätzliches Autorisierungsverhalten zu implementieren, falls ein höherer Anpassungsgrad gewünscht ist. Der folgende Code zeigt einen benutzerdefinierten Autorisierungsfilter:

public class RequireAdminClaimAttribute : AuthorizeAttribute
{
  protected override bool IsAuthorized(HttpActionContext context)
  {
    var principal =
      context.Request.GetRequestContext().Principal as ClaimsPrincipal;
    return principal.Claims.Any(c => c.Type ==
      "http://yourschema/identity/claims/admin"
      && c.Value == "true");
  }
}

Mithilfe dieses Filters wird einfach der benutzerdefinierte Anspruch "admin" gesucht, Sie können jedoch auch einen Prinzipal und zusätzliche Informationen aus "HttpActionContext" verwenden, um an dieser Stelle eine benutzerdefinierte Autorisierung auszuführen.

In der ersten Version der ASP.NET-Web-API wurden benutzerdefinierte Autorisierungsfilter oft fälschlicherweise zur Implementierung der Authentifizierung eingesetzt. Bei der ASP.NET-Web-API 2 verfügen die Authentifizierungsfilter jetzt jedoch über einen eigenen Platz in der Pipeline, was zur Entwicklung eines sauberen, modularen Codes mit klar getrennten Authentifizierungs- und Autorisierungsaspekten beiträgt.

Überschreibungsfilter

Wie bereits oben erläutert, kann ein Autorisierungsfilter auf der Ebene der Aktionsmethode, der Ebene des Controllers oder global angewendet werden. Indem Sie den Authorize-Filter global angeben, können Sie die Autorisierung controllerübergreifend für alle Aktionsmethodenaufrufe durchsetzen. Wenn einige Methoden aus der global konfigurierten Überprüfung ausgeschlossen werden sollen, lässt sich dies einfacher mithilfe des AllowAnonymous-Attributs erreichen.

Der Code in Abbildung 5 zeigt die Verwendung des AllowAnonymous-Attributs auf der Controllerebene. Obwohl der Autorisierungsfilter global angewendet wird, schließt das mit "PublicResourcesController" verwendete AllowAnonymous-Attribut die an diesen Controller übermittelten Anforderungen von der Autorisierung aus.

Abbildung 5 – Verwenden des Attributs "AllowAnonymous"

public static class WebApiConfig
{
  public static void Register(HttpConfiguration config)
  {
    // Other Web API configuration code goes here
    config.Filters.Add(new AuthorizeAttribute()); // Global level
  }
}
[AllowAnonymous]
public class PublicResourcesController : ApiController
{
  public string Get(int id)
  {
    return “Hello World”;
  }
}

"AllowAnonymous" bietet eine Möglichkeit, wie eine spezifische Aktion die von einem höheren Autorisierungsfilter konfigurierte Autorisierung überschreiben kann. "AllowAnonymous" lässt jedoch nur das Überschreiben der Autorisierung zu. Stellen Sie sich vor, Sie möchten die meisten Aktionen mithilfe der "HTTP Basic"-Authentifizierung, aber eine Aktion mithilfe von Tokens authentifizieren lassen. Es wäre gut, wenn Sie die Tokenauthentifizierung global konfigurieren, aber die Authentifizierung dieser einen Aktion überschreiben könnten, etwa wie eine Autorisierung mit "AllowAnonymous" überschrieben wird.

Mit ASP.NET-Web-API 2 wird ein neuer Filtertyp für dieses Szenario eingeführt: der Überschreibungsfilter. Im Unterschied zu "AllowAnonymous" funktionieren die mit ASP.NET-Web-API 2 eingeführten Überschreibungsfilter mit beliebigen Filtertypen. Wie der Name schon sagt, überschreiben Überschreibungsfilter die auf höheren Ebenen konfigurierten Authentifizierungsfilter. Dazu wird das direkt einsetzbare OverrideAuthentication-Attribut verwendet. Wenn Sie einen Authentifizierungsfilter global angewendet haben und diesen nicht mehr für eine bestimmte Aktionsmethode bzw. einen bestimmten Controller ausführen möchten, können Sie "OverrideAuthentication" einfach auf der gewünschten Ebene anwenden.

Überschreibungsfilter bieten jedoch weitaus mehr, als nur die Ausführung bestimmter Filter zu verhindern. Angenommen, Sie verfügen über zwei Authentifizierungsfilter, einen für die Authentifizierung eines Sicherheitstokens und einen anderen für die Authentifizierung eines Benutzernamens/Kennworts in einem "HTTP Basic"-Schema. Beide Filter werden global angewendet, wodurch die API flexibel entscheiden kann, ob sie das Token oder Benutzernamen/Kennwort akzeptiert. Der folgende Code zeigt die beiden global angewendeten Authentifizierungsfilter:

public static class WebApiConfig
{
  public static void Register(HttpConfiguration config)
  {
    // Other Web API configuration code goes here
    config.Filters.Add(new TokenAuthenticator());
    config.Filters.Add(new HttpBasicAuthenticator(realm: "Magical"));
  }
}

Jetzt möchten Sie vielleicht sicherstellen, dass nur das Token als Anmeldeinformation für den Zugriff auf eine bestimmte Aktionsmethode verwendet wird. Wie kann Ihnen "OverrideAuthentication" helfen, diese Anforderung zu erfüllen, wenn es lediglich die Ausführung von Filtern verhindert? Hier kommen wir zu einem wichtigen Merkmal der Überschreibungsfilter – sie löschen zwar alle auf höheren Ebenen angegebenen Filter, entfernen jedoch keine Filter auf derselben Ebene wie sie selbst. Das bedeutet, dass Sie im Grunde einen oder mehrere Authentifizierungsfilter auf einer spezifischen Ebene hinzufügen können, während Sie alle Filter der höheren Ebenen löschen. In Bezug auf die Anforderung, dass nur das Token als Anmeldeinformation für den Zugriff auf eine bestimmte Aktionsmethode verwendet werden kann, können Sie einfach das OverrideAuthentication-Attribut und "TokenAuthenticator" auf der Ebene der Aktionsmethode angeben, wie im folgenden Code dargestellt (dadurch wird sichergestellt, dass nur "TokenAuthenticator" für die Aktionsmethode "GetAllowedForTokenOnly" ausgeführt wird):

public class EmployeesController : ApiController
{
  [OverrideAuthentication] // Removes all authentication filters
  [TokenAuthenticator] // Puts back only the token authenticator
  public string GetAllowedForTokenOnly(int id)
  {
    return “Hello World”;
  }
}

Die mit ASP.NET-Web-API 2 eingeführten Überschreibungsfilter bieten folglich eine deutlich höhere Flexibilität: zum einen bei der globalen Festlegung von Filtern und zum anderen bei der selektiven Ausführung von Filtern auf niedrigeren Ebenen, für die das globale Verhalten überschrieben werden soll.

Zusätzlich zum OverrideAuthentication-Attribut steht auch ein direkt einsetzbares Attribut mit dem Namen "OverrideAuthorization" zur Verfügung, das die auf höheren Ebenen angegebenen Autorisierungsfilter entfernt. Im Vergleich zu "AllowAnonymous" entfernt "OverrideAuthorization" jedoch nur die Autorisierungsfilter höherer Ebenen. Autorisierungsfilter auf derselben Ebene werden nicht entfernt. "AllowAnonymous" sorgt dafür, dass die ASP.NET-Web-API den gesamten Autorisierungsprozess überspringt – auch wenn Autorisierungsfilter auf derselben Ebene wie "AllowAnonymous" angegeben wurden. Sie werden einfach ignoriert.

Zusammenfassung

Sie haben zwei Möglichkeiten, die Authentifizierung in der ASP.NET-Web-API zu implementieren: über die vom Host bereitgestellten Optionen oder die von der ASP.NET-Web-API-Pipeline bereitgestellten Erweiterungspunkte. Hostbasierte Optionen fügen sich nahtlos in die Hostpipeline ein und können ungültige Anforderungen weiter oben in der Pipeline ablehnen. ASP.NET-Web-API-Erweiterungspunkte ermöglichen eine differenziertere Steuerung des Authentifizierungsprozesses. Wenn Sie mehr Kontrolle über die Authentifizierung erhalten möchten – z. B. verschiedene Authentifizierungsmechanismen für unterschiedliche Controller oder sogar unterschiedliche Aktionsmethoden verwenden möchten – besteht die richtige Lösung darin, Authentifizierungsaspekte über einen Authentifizierungsfilter und Autorisierungsaspekte über einen Autorisierungsfilter zu implementieren.


Badrinarayanan Lakshmiraghavan ist leitender, beratend tätiger Systemarchitekt bei einem Fortune 500-Unternehmen. Er ist der Autor von Büchern wie "Pro ASP.NET Web API Security" und "Practical ASP.NET Web API", beide 2013 bei Apress erschienen. Gelegentlich bloggt er unter lbadri.wordpress.com.

Unser Dank gilt dem folgenden technischen Experten von Microsoft für die Durchsicht dieses Artikels: David Matson
David Matson ist als Softwareentwickler bei Microsoft tätig. Er gehört dem MVC-5- und Web-API-2-Produktteam an.