Volím Svobodné

Modul pro 'basic' autentizaci v ASP.NET

Jak funguje 'basic authentication'

Metoda 'basic' je nejstarší a nejjednodušší metoda HTTP autentizace. Je přímo součástí HTTP protokolu a podporují ji všechny prohlížeče, proxy servery a podobně. Není tedy třeba podpora cookies a podobně. Pokud se pokusíte přistoupit na nějakou stránku chráněnou "basic" autentizací, zobrazí vám prohlížeč systémový dialog pro zadání jména a hesla - celá autentizace se tak děje zcela v jeho režii, vaše aplikace pro ni nevytváří žádné GUI.

Technicky vzato funguje HTTP autentizace následovně.

  1. Klient se pokusí přistoupit na zabezpečenou stránku.
  2. Server odpoví chybovým kódem 401 Access Denied a do odpovědi přidá hlavičku WWW-Authenticate: Basic Realm="Název aplikace".
  3. Klient si od uživatele vyžádá jméno a heslo a pokus opakuje. Tentokráte ale přidá do požadavku hlavičku Authorize: Basic username:password. Uživatelské jméno a heslo je odděleno dvojtečkou a celý tento řetězec je zakódován ve formátu Base64.
  4. Server ověří uživatelské jméno a heslo. Pokud je správné, vydá kýžený obsah. Není-li, opakuje se bod 2.

Ze shora uvedeného vyplývají dvě hlavní nevýhody basic autentizace:

  • S každým požadavkem na zabezpečenou stránku je zasíláno uživatelské jméno a heslo v nezašifrovaném tvaru (Base64 je přenosové kódování, nikoliv šifra). To představuje bezpečností riziko, a proto je obecně vhodné používat tuto formu autentizace pouze na šifrovaném kanále (na webu zabezpečeném pomocí HTTPS).
  • Přihlášení je nutno si vynutit. Pokud o něj server explicitně nepožádá, klient mu ho nepošle. Není tedy možno vytvořit jednu stránku, která bude zobrazovat jiný obsah pro přihlášeného a jiný pro anonymního uživatele.

Implementace v ASP.NET

V prostředí internetových aplikací na platformě Windows se tato forma autentizace moc neuchytila. Hlavním důvodem je podle mého soudu skutečnost, že pomocí standardně dostupných prostředků je možno ověřovat se pouze proti systémovým účtům (uživatelům ve Windows nebo Active Directory), nikoliv např. proti vlastní databázi uživatelů.

Je ale možné jednoduše napsat autentizační modul, který bude basic autentizaci podporovat. Technicky vzato se jedná o třídu implementující rozhraní System.Web.IHttpModule a obsluhující události OnAuthenticateRequest a OnEndRequest.

Je důležité si uvědomit, že je nutné vypnout všechny ostatní formy autentizace. Tj. v Internet Information Services u webu povolit pouze anonymní přístup (jinak by se o ověřování snažilo IISko samo) a v konfiguraci ASP.NET (web.config) nastavit /configuration/system.web/authentication/@mode na hodnotu None.

Proof-of-concept

Vyzkoušíme si, zda popsaná metoda úspěšně funguje, níže najdete proof-of-concept kód, který vyžaduje uživatelské jméno user a heslo password. Protentokrát je napsaný v C#, aby se mi zase na VSNET-L nepošklebovali ;-)

				

using System;
using System.Web;

public class BasicPOC: IHttpModule {

    public void Dispose() {
        // Nothing to dispose
    }

    public void Init(HttpApplication application) {
        // Attach event handlers
        application.AuthenticateRequest += new EventHandler(this.OnAuthenticateRequest);
        application.EndRequest += new EventHandler(this.OnEndRequest);
    }

    public void OnAuthenticateRequest(object source, EventArgs eventArgs) {
        HttpApplication Application = (HttpApplication)source;

        // Get authentication data
        string AuthString = Application.Request.Headers["Authorization"];
        if (String.IsNullOrEmpty(AuthString)) return; // anonymous request
        if (!AuthString.StartsWith("Basic", StringComparison.InvariantCultureIgnoreCase)) return; // not basic auth

        // Get username and password
        string UserName, Password;
        try {
            AuthString = System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(AuthString.Substring(6)));
            string[] AuthData = AuthString.Split(new char[] { ':' }, 2);
            UserName = AuthData[0];
            Password = AuthData[1];
        }
        catch (Exception ex) {
            throw new System.Security.SecurityException("Invalid format of 'Authorization' HTTP header.", ex);
        }

        // Validate user against currently configured membership provider
        if (UserName == "user" && Password == "password") {
            // Success
            Application.Context.User = new System.Security.Principal.GenericPrincipal(new System.Security.Principal.GenericIdentity(UserName), new string[0]);
        }
        else {
            // Failure
            Application.Response.StatusCode = 401;
            Application.Response.StatusDescription = "Access Denied";
            Application.Response.Write("<html>\n<head><title>401 Access Denied</title></head>\n<body><h1>401 Access Denied</h1></body>\n</html>");
            Application.CompleteRequest();
            return;
        }
    }

    public void OnEndRequest(object source, EventArgs eventArgs) {
        // Add WWW-Authenticate header if access denied
        HttpApplication Application = (HttpApplication)source;
        if (Application.Response.StatusCode == 401) Application.Response.AppendHeader("WWW-Authenticate", "Basic Realm=\"Moje aplikace\"");
    }
}

Bližší popis fungování HTTP modulů najdete v článku HTTP moduly prakticky. Veškerou špinavou práci dělají metody OnAuthenticateRequest a OnEndRequest.

První jmenovaná dekóduje a ověří HTTP hlavičku Authorization, pokud je přítomna. Povšimněte si prosím, že v případě, že přítomna není (uživatel je anonymní) nic nedělá (nevrací žádnou chybu nebo stavový kód 401 Access Denied). O tom, zda se bude přihlášení vyžadovat, totiž nerozhoduje náš modul, ale autorizační modul - typicky na základě nastavení v souboru web.config. Jediný případ, kdy náš modul proaktivně zakazuje přístup, je když uživatel sice zadá jméno a heslo, ale chybné.

Metoda OnEndRequest zařizuje jedinou věc: pokud je na konci zpracování požadavku nastaven stavový kód 401 Access Denied, přidá do výstupu zmiňovanou HTTP hlavičku WWW-Authenticate. Pokud kdokoliv (náš modul, autorizační modul, jiná část aplikace...) bude pomocí tohoto kódu vyžadovat přihlášení, bude k němu uživatel vyzván.

Pomocí následujícího nastavení v souboru web.config modul aktivujeme a zároveň zakážeme do celé aplikace přístup anonymním uživatelům, takže se vynutí přihlášení:

<?xml version="1.0"?>
<configuration>
  <system.web>
    <httpModules>
      <add name="BasicPOC" type="BasicPOC" />
    </httpModules>
    <compilation debug="true" />
    <authentication mode="None" />
    <authorization>
      <deny users="?"/>
    </authorization>
  </system.web>
</configuration>

Plná implementace

Mým cílem je vytvořit univerzální modul, který bude možno snadno nasadit a integrovat ho se strukturou membership providerů v ASP.NET a podobně. Zároveň chci, aby byl konfigurovatelný Realm a obsah stránky, která se zobrazí v případě chybného přihlášení.

Vytvořil jsem proto následující strukturu tříd:

Class diagram 

  • Altairis.Web.Security.BasicAuthentication.HttpModule je výše zmíněný autentizační modul. Jeho kód se změni jenom nepatrně: místo napevno zakódovaného jména a hesla volám metodu Memebrship.ValidateUser(), která mi ověří jméno a heslo proti aktivnímu membership providerovi. A místo pevně určeného názvu realmu a tetxu chybové stránky tyto hodnoty načítám z konfigurace.
  • Altairis.Web.Security.BasicAuthentication.ConfigurationSectionHandler je handler konfigurační sekce, který mi umožní přidat si do souboru web.config vlastní konfigurační parametry. Podrobnější popis najdete v článku Vytvoření vlastní konfigurační sekce ve Web.Config.
  • Altairis.Web.Security.BasicAuthentication.ConfigurationSettings je strukura, která uchovává konfigurační informace a spolupracuje s předchozí třídou.

Odkazy a kód ke stažení

Titulek:
Text komentáře:
Vaše jméno:
Váš e-mail: (nebude zveřejněn)

WWW stránka:
Opište text z obrázku:
odpovědětodpovědět Gravatar

RE: Jak funguje 'basic authentication'

Pekny clanok :-)

  • Altairis
  • Nemesis
  • Microsoft MVP
  • IIS
  • ASP.NET