Basic Authentication mit ASP.NET
Möglichkeiten der Authentifizierung mit ASP.NET
Mit ASP.NET gibt es verschiedene Möglichkeiten, sich am Server anzumelden, die bekannteste davon ist die Möglichkeit über Membership-API von ASP.NET.
Zudem gibt es noch die Möglichkeiten, sich direkt am IIS zu authentifizieren, sei dies über "Integrated", "Digest" oder "Basic".
Bei einer Autentifizierung über die Membership-API braucht es ein grosses Gerüst, eine Datenquelle sowie mindestens einen Membership-Provider, optional noch einen Role-Provider.
Wenn keine Datenbank verfügbar ist, bzw. keine die direkt von der Membership-API unterstützt wird, muss ein eigener Provider geschrieben werden, dies gibt einen Mehraufwand und ist nicht in allen Fällen die richtige Lösung.
Die Membership-API bietet standardmässig nur die Autentifizierungsoptionen "None, Forms, Windows oder Passport.
All diese Möglichkeiten benötigen in der Standardkonfiguration eine GUI für die Anmeldung und Verwaltung.
An dieser Stelle sollte noch die Möglichkeit erwähnt werden, eine eigene API für die Benutzerverwaltung zu schreiben, falls sich die Membership-API nicht lohnt / zu gross ist.
Ein Beispiel finden sie auf dem Blog von Jürgen Gutsch: Einfache Authentifizierung mit ASP.NET.
Authentifizierung ohne eigene GUI
Mit eigenen HttpHandlers lassen sich nicht nur Ressourcen-Downloads abbilden (Dateien, CSS, Javascript, ..), sondern auch komplette Webseiten.
(Merke: Die Basisklasse für eine ASP.NET Seite _Page_ ist auch ein HttpHandler, sprich: implementiert auch das Interface IHttpHandler)
Eine Möglichkeit wie sowas umgesetzt werden könnte, wurde in diesem Blog schon mal erwähnt, daneben könnte eine komplette Webapplikation mithilfe einer HttpHandlerFactory (IHttpHandlerFactory) aufgebaut werden, dazu aber in einem anderem Beitrag mehr.
Wenn jetzt ein fertiger HttpHandler besteht, der von der eigentlichen Seiteninfrastruktur losgelöst ist und dessen Aufrufe Passwort geschützt werden müssen, was dann?
Bei keiner Loslösung zur eigentlichen Applikation kann dessen Authentifizierung genutzt werden - also eine Anmeldung am System und dann Zugriff mittels Session oder Cookie.
Ist dies aber nicht der Fall, kann diese Lücke mit der HTTP Basic Autentification gefüllt werden.
Basic Authentification?
Das HTTP-Protokoll selber sieht eine einfache Möglichkeit zur Authentifizierung vor, die Basic Authentifizierung.
Wenn auf eine Ressource zugegriffen wird, die geschützt ist, wird beispielsweise folgender Header zum Browser geschickt:
HTTP/1.1 401 Unauthorized
Server: Microsoft-IIS/6.0
Date: Fr, 17 Oct 2008 10:22:44 GMT
WWW-Authenticate: BASIC Realm=Download Area
Connection: Keep-Alive
Content-Length: 443
Content-Type: application/pdf
Cache-control: private
Der Statuscode "401 Unauthorized" + der Header "WWW-Authenticate" + "Basic " sagt dem Browser, dass er ein Loginfenster, wie vom guten alten Verzeichnisschutz mit dem IIS bekannt ist, darstellen soll.
Dort wird ein Benutzername + Passwort eingegeben und bei Bestätigung durch [OK] zum Server gesendet.
Das Versenden geschieht im Klartext, die Benutzernamen- Passwortkombination ist lediglich Base64 codiert.
Auf dem Server wird die Kombination wiederum encodiert und geprüft.
Falls die Prüfung erfolgreich ist, wird anstelle des beschrieben Statuscodes sowie dem WWWAuthenticate-Header der eigentliche Inhalt zum Client geschickt.
Soweit die einfache Ausführung. Wie bei einer einfachen Authentifizierung mit ASP.NET kann auch hier Gebrauch vom Cookies gemacht werden, um so eine Anmeldung für einen weiteren Besuch aufrecht zu erhalten.
Eine Aufrechterhaltung mithilfe des SessionStates ist nicht nötig, da sich die Browser das letze Login merken und daher für die Dauer einer Sitzung keine weitere Eingabe mehr nötig ist, wenn nicht eine andere Kennung erforderlich ist.
Hinweis:
Beim IIS7 funktioniert das beschriebene Vorgehen in der Standardeinstellung, beim IIS6 hingegen ist in der Standardeinstellung "Integrated Windows Authentication" an, dann versucht der IIS die Anfrage selbst zu bearbeiten.
Bei den meisten Web-Providern sollte diese Einstellung standardmässig aus sein - einfach mal probieren.
Beispiel?
Folgend werden sie in ein Beispiel eingeführt das die Downloads im App_Code-Ordner hält (Dadurch vom Client nicht direkt abrufbar, also geschützt) und Zugriff auf die Downloads durch einen Handler gegen Eingabe der richtigen Benutzernamen- / Passwortkombination gewährt.
Der HttpHandler im Beispiel hat zwei Methoden, neben der Implementierung vom IHttpHandler-Interface.
Wenn ein Request eintrifft, werden in der Methode "CheckLogin" jeweils die Daten in der Servervariable "HTTP_AUTHORIZATION" abgefragt.
Diese Daten enthalten im Falle einer Übergabe von Logindaten (Also wenn der Benutzer Benutzername und Passwort eingegeben hat) beispielsweise folgende Zeichenfolge:
Basic YWRtaW46cHdk
"Basic " ist die Kennung, dass es sich um die Basic Authentication handelt und die Zeichenfolge dahinter ist der Benutzername und das Password mit Base64 codiert.
Wenn diese Zeichenfolge decodiert wird, könnte sie beispielsweise so aussehen:
admin:pass
Aufgrund dieser Daten wird das Login überprüft, bei Erfolg die Datei zum Client geschickt und bei Misserfolg wiederum im Header die Daten geschickt, dass der Client sich identifizieren muss.
Dabei ist nur der Statuscode 401 + der Header nötig, die Ausgabe per Response.Write() ist optional.
"Realm" steht für den Bereich der gesichert wird, im Beispiel einfach "Download Area".
namespace BasicAuthExample.Classes
{
public class SecureHandler : IHttpHandler
{
#region IHttpHandler Members
public void ProcessRequest(HttpContext context) {
// Login Checken
if(CheckLogin(context)) {
// Datei zum Client schicken
context.Response.ContentType = "application/octet-stream";
context.Response.AddHeader("content-disposition", "attachment; filename=test.txt");
context.Response.WriteFile(context.Server.MapPath("~/App_Data/secure.txt"));
} else {
// Authentifizierung anfordern
RequestAuth(context);
}
}
public bool IsReusable {
get {
return false;
}
}
#endregion
private bool CheckLogin(HttpContext context) {
// Infos vom Client (Name / Passwort)
string data = context.Request.ServerVariables["HTTP_AUTHORIZATION"] ?? String.Empty;
// Sind Daten einer Basic Authentifizierung vorhanden?
bool isAuthenticationDataAvailable =
data != String.Empty && data.IndexOf("Basic") > -1;
// Wenn keine Daten vorhanden sind, ist das Login fehlgeschlagen
if (!isAuthenticationDataAvailable) {
return false;
}
// Ansonsten Passwort und Namen decodieren und extrahieren
string encodedAsBase64 = data.Replace("Basic ", String.Empty);
byte[] plainText = Convert.FromBase64String(encodedAsBase64);
UTF8Encoding encoding = new UTF8Encoding();
string encodedAsUtf8 = encoding.GetString(plainText);
// Checken ob ein Doppelpunkt vorhanden ist
int indexOfColon = encodedAsUtf8.IndexOf(':');
if (indexOfColon == -1) {
return false;
}
// Ansonsten Passwort und Namen decodieren und extrahieren
string username = encodedAsUtf8.Substring(0, indexOfColon);
string password = encodedAsUtf8.Substring(indexOfColon + 1);
// Checken ob das Login korrekt ist
if (username.Equals("admin") && password.Equals("pass")) {
return true;
}
// Login fehlgeschlagen
return false;
}
private void RequestAuth(HttpContext context) {
context.Response.Write("401 Unauthorized");
context.Response.Status = "401 Unauthorized";
context.Response.AddHeader("WWW-Authenticate", "BASIC Realm=Download Service");
context.Response.End();
}
}
}
Wann ist diese Art der Authentifizierung jetzt überhaupt sinnvoll?
Diese Frage ist durch den ersten Teil des Artikels schon fast beantwortet.
Bei einem HttpHandler - vorallem wenn er selber von der Hauptapplikation isoliert ist (Externe Library beispielsweise) - ist es nicht einfach möglich oder sinnvoll für die Anmeldung eine riesen Infrastruktur inklusive GUI hochzufahren.
Genau bei einem solchen Fall, ist die Basic Authentifizierung ein guter Kandidat.
Wenn es um wirklich sichere Daten geht, sieht diese Art der Authentifizierung jedoch alt aus, sofern keine SSL-Verschlüsselung im Spiel ist, da alles im Klartext übermittelt wird.
Abschliessende Worte
Ich hoffe dieser Ausflug in die Authentifizierung mit ASP.NET bringt auch bei diesem Thema ein bisschen Licht ins Dunkle
und wirft hoffentlich auch neue Fragen, Ideen und Diskussionen auf.
Wie immer bin ich für alle Ideen, Vorschläge und jegliche konstruktive Kritik gerne zu haben, benutzt dazu einfach die Kommentarmöglichkeit dieses Blogs.
Download des Beispiels:
Quellen:
Bearbeitung / Korrekturen:
08.01.09 - CheckLogin-Methode verbessert und Download upgedated