Zabezpečení webového rozhraní API pomocí jednotlivých účtů a místního přihlášení v ASP.NET webovém rozhraní API 2.2

Mike Wasson

Stažení ukázkové aplikace

Toto téma ukazuje, jak zabezpečit webové rozhraní API pomocí OAuth2 k ověření v databázi členství.

Verze softwaru používané v tomto kurzu

V sadě Visual Studio 2013 poskytuje šablona projektu webového rozhraní API tři možnosti ověřování:

  • Jednotlivé účty. Aplikace používá databázi členství.
  • Účty organizace. Uživatelé se přihlašují pomocí svých přihlašovacích údajů Azure Active Directory, Office 365 nebo místních přihlašovacích údajů active directory.
  • Ověřování systému Windows. Tato možnost je určená pro intranetové aplikace a používá modul IIS pro ověřování systému Windows.

Jednotlivé účty poskytují uživatelům dva způsoby přihlášení:

  • Místní přihlášení. Uživatel se zaregistruje na webu a zadá uživatelské jméno a heslo. Aplikace ukládá hodnotu hash hesla do databáze členství. Když se uživatel přihlásí, systém identit ASP.NET ověří heslo.
  • Přihlášení k sociální sadě. Uživatel se přihlásí pomocí externí služby, jako je Facebook, Microsoft nebo Google. Aplikace stále vytvoří položku pro uživatele v databázi členství, ale neukládá žádné přihlašovací údaje. Uživatel se ověřuje přihlášením k externí službě.

Tento článek se zabývá scénářem místního přihlášení. Pro místní i sociální přihlášení používá webové rozhraní API k ověřování požadavků OAuth2. Toky přihlašovacích údajů se ale pro místní a sociální přihlášení liší.

V tomto článku si předvedu jednoduchou aplikaci, která uživateli umožní přihlásit se a odesílat ověřená volání AJAX do webového rozhraní API. Vzorový kód si můžete stáhnout tady. Soubor readme popisuje, jak v sadě Visual Studio vytvořit zcela novou ukázku.

Obrázek ukázkového formuláře

Ukázková aplikace používá Knockout.js pro datové vazby a jQuery pro odesílání požadavků AJAX. Zaměřím se na volání AJAX, takže nemusíte znát Knockout.js pro tento článek.

Po cestě popíšem:

  • Co aplikace dělá na straně klienta.
  • Co se děje na serveru.
  • Provoz HTTP uprostřed.

Nejprve musíme definovat určitou terminologii OAuth2.

  • Zdroj. Některá data, která je možné chránit.
  • Server prostředků. Server, který je hostitelem prostředku.
  • Vlastník prostředku Entita, která může udělit oprávnění pro přístup k prostředku. (Obvykle uživatel.)
  • Klient: Aplikace, která chce přístup k prostředku. V tomto článku je klient webovým prohlížečem.
  • Přístupový token. Token, který uděluje přístup k prostředku.
  • Nosný token. Konkrétní typ přístupového tokenu s vlastností, kterou může tento token používat každý. Jinými slovy, klient nepotřebuje kryptografický klíč ani jiný tajný kód, aby používal nosný token. Z tohoto důvodu by nosné tokeny měly být použity pouze přes PROTOKOL HTTPS a měly by mít relativně krátkou dobu vypršení platnosti.
  • Autorizační server. Server, který poskytuje přístupové tokeny.

Aplikace může fungovat jako autorizační server i server prostředků. Šablona projektu webového rozhraní API se řídí tímto vzorem.

Tok přihlašovacích údajů místního přihlášení

Pro místní přihlášení používá webové rozhraní API tok hesla vlastníka prostředku definovaný v OAuth2.

  1. Uživatel zadá do klienta jméno a heslo.
  2. Klient odešle tyto přihlašovací údaje na autorizační server.
  3. Autorizační server ověří přihlašovací údaje a vrátí přístupový token.
  4. Pro přístup k chráněnému prostředku klient zahrne přístupový token v autorizační hlavičce požadavku HTTP.

Diagram toku přihlašovacích údajů místního přihlášení

Když v šabloně projektu webového rozhraní API vyberete jednotlivé účty , projekt obsahuje autorizační server, který ověřuje přihlašovací údaje uživatele a vydává tokeny. Následující diagram znázorňuje stejný tok přihlašovacích údajů z hlediska komponent webového rozhraní API.

Diagram, když jsou na webu A P I vybrány jednotlivé účty

V tomto scénáři fungují řadiče webového rozhraní API jako servery prostředků. Ověřovací filtr ověřuje přístupové tokeny a atribut [Authorize] slouží k ochraně prostředku. Pokud má kontroler nebo akce atribut [Authorize] (Autorizovat), musí být všechny požadavky na tento kontroler nebo akci ověřeny. Jinak se autorizace odepře a webové rozhraní API vrátí chybu 401 (Neautorizováno).

Autorizační server i ověřovací filtr obě volají do komponenty middlewaru OWIN, která zpracovává podrobnosti OAuth2. Podrobněji popíšem návrh později v tomto kurzu.

Odeslání neautorizované žádosti

Začněte tím, že aplikaci spustíte a kliknete na tlačítko Rozhraní API pro volání. Po dokončení požadavku by se v poli Výsledek měla zobrazit chybová zpráva. Důvodem je to, že požadavek neobsahuje přístupový token, takže požadavek není neoprávněný.

Obrázek chybové zprávy výsledku

Tlačítko Rozhraní API volání odešle požadavek AJAX na hodnotu ~/api/values, která vyvolá akci kontroleru webového rozhraní API. Tady je část kódu JavaScriptu, která odešle požadavek AJAX. V ukázkové aplikaci se veškerý kód javascriptové aplikace nachází v souboru Scripts\app.js.

// If we already have a bearer token, set the Authorization header.
var token = sessionStorage.getItem(tokenKey);
var headers = {};
if (token) {
    headers.Authorization = 'Bearer ' + token;
}

$.ajax({
    type: 'GET',
    url: 'api/values/1',
    headers: headers
}).done(function (data) {
    self.result(data);
}).fail(showError);

Dokud se uživatel nepřihlásí, neexistuje nosný token, a proto v požadavku není žádná autorizační hlavička. To způsobí, že požadavek vrátí chybu 401.

Tady je požadavek HTTP. (Použil (a) jsemFiddler zachytí provoz HTTP.)

GET https://localhost:44305/api/values HTTP/1.1
Host: localhost:44305
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0
Accept: */*
Accept-Language: en-US,en;q=0.5
X-Requested-With: XMLHttpRequest
Referer: https://localhost:44305/

Odpověď HTTP:

HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
WWW-Authenticate: Bearer
Date: Tue, 30 Sep 2014 21:54:43 GMT
Content-Length: 61

{"Message":"Authorization has been denied for this request."}

Všimněte si, že odpověď obsahuje hlavičku Www-Authenticate s výzvou nastavenou na Bearer. To znamená, že server očekává nosný token.

Registrace uživatele

V části Registrace aplikace zadejte e-mail a heslo a klikněte na tlačítko Zaregistrovat.

Pro tuto ukázku nemusíte používat platnou e-mailovou adresu, ale skutečná aplikace tuto adresu potvrdí. (Viz Vytvořte zabezpečenou webovou aplikaci ASP.NET MVC 5 s přihlášením, potvrzením e-mailu a resetováním hesla.) Pro heslo použijte něco jako "Heslo1!", s velkým písmenem, velkým písmenem, číslicí a číslicemi bez alfanumerické znaky. Aby byla aplikace jednoduchá, vynechám ověřování na straně klienta, takže pokud je problém s formátem hesla, zobrazí se chyba 400 (Chybný požadavek).

Obrázek oddílu registrace uživatele

Tlačítko Zaregistrovat odešle požadavek POST na adresu ~/api/Account/Register/. Text požadavku je objekt JSON, který obsahuje název a heslo. Tady je javascriptový kód, který odešle požadavek:

var data = {
    Email: self.registerEmail(),
    Password: self.registerPassword(),
    ConfirmPassword: self.registerPassword2()
};

$.ajax({
    type: 'POST',
    url: '/api/Account/Register',
    contentType: 'application/json; charset=utf-8',
    data: JSON.stringify(data)
}).done(function (data) {
    self.result("Done!");
}).fail(showError);

Požadavek HTTP, kde $CREDENTIAL_PLACEHOLDER$ je zástupný symbol pro pár klíč-hodnota hesla:

POST https://localhost:44305/api/Account/Register HTTP/1.1
Host: localhost:44305
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0
Accept: */*
Content-Type: application/json; charset=utf-8
X-Requested-With: XMLHttpRequest
Referer: https://localhost:44305/
Content-Length: 84

{"Email":"alice@example.com",$CREDENTIAL_PLACEHOLDER1$,$CREDENTIAL_PLACEHOLDER2$"}

Odpověď HTTP:

HTTP/1.1 200 OK
Server: Microsoft-IIS/8.0
Date: Wed, 01 Oct 2014 00:57:58 GMT
Content-Length: 0

Tento požadavek zpracovává AccountController třída. Interně AccountController používá ASP.NET Identity ke správě databáze členství.

Pokud aplikaci spustíte místně ze sady Visual Studio, uživatelské účty se ukládají v LocalDB v tabulce AspNetUsers. Pokud chcete zobrazit tabulky v sadě Visual Studio, klikněte na nabídku Zobrazení , vyberte Průzkumník serveru a potom rozbalte datové připojení.

Obrázek datových připojení

Získání přístupového tokenu

Zatím jsme neprovedli žádnou OAuth, ale teď uvidíme autorizační server OAuth v akci, když požádáme o přístupový token. V oblasti Přihlášení ukázkové aplikace zadejte e-mail a heslo a klikněte na Přihlásit se.

Obrázek oddílu přihlášení

Tlačítko Přihlásit se odešle požadavek na koncový bod tokenu. Tělo požadavku obsahuje následující data zakódovaná pomocí adresy URL formuláře:

  • grant_type: "password"
  • uživatelské jméno: <e-mail uživatele>
  • heslo: <heslo>

Tady je javascriptový kód, který odešle požadavek AJAX:

var loginData = {
    grant_type: 'password',
    username: self.loginEmail(),
    password: self.loginPassword()
};

$.ajax({
    type: 'POST',
    url: '/Token',
    data: loginData
}).done(function (data) {
    self.user(data.userName);
    // Cache the access token in session storage.
    sessionStorage.setItem(tokenKey, data.access_token);
}).fail(showError);

Pokud požadavek proběhne úspěšně, autorizační server vrátí přístupový token v textu odpovědi. Všimněte si, že token uložíme v úložišti relací, abychom ho mohli později použít při odesílání požadavků do rozhraní API. Na rozdíl od některých forem ověřování (například ověřování na základě souborů cookie) nebude prohlížeč do následných požadavků automaticky zahrnovat přístupový token. Aplikace to musí provést explicitně. To je dobrá věc, protože omezuje ohrožení zabezpečení CSRF.

Požadavek HTTP:

POST https://localhost:44305/Token HTTP/1.1
Host: localhost:44305
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: https://localhost:44305/
Content-Length: 68

grant_type=password&username=alice%40example.com&password=Password1!

Vidíte, že požadavek obsahuje přihlašovací údaje uživatele. K zajištění zabezpečení přenosové vrstvy musíte použít protokol HTTPS.

Odpověď HTTP:

HTTP/1.1 200 OK
Content-Length: 669
Content-Type: application/json;charset=UTF-8
Server: Microsoft-IIS/8.0
Date: Wed, 01 Oct 2014 01:22:36 GMT

{
  "access_token":"imSXTs2OqSrGWzsFQhIXziFCO3rF...",
  "token_type":"bearer",
  "expires_in":1209599,
  "userName":"alice@example.com",
  ".issued":"Wed, 01 Oct 2014 01:22:33 GMT",
  ".expires":"Wed, 15 Oct 2014 01:22:33 GMT"
}

Kvůli čitelnosti jsem odsadil JSON a zkrátil přístupový token, což je poměrně dlouhé.

Vlastnost access_tokena vlastnosti token_typeexpires_in jsou definovány specifikací OAuth2. Ostatní vlastnosti (userName, .issueda .expires) jsou určené pouze pro informační účely. Kód, který tyto další vlastnosti přidá do metody, najdete v TokenEndpoint souboru /Providers/ApplicationOAuthProvider.cs.

Odeslání ověřeného požadavku

Když teď máme nosný token, můžeme do rozhraní API vytvořit ověřený požadavek. To se provádí nastavením autorizační hlavičky v požadavku. Klikněte znovu na tlačítko Rozhraní API pro volání, aby se zobrazilo.

Obrázek po kliknutí na tlačítko A P

Požadavek HTTP:

GET https://localhost:44305/api/values/1 HTTP/1.1
Host: localhost:44305
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0
Accept: */*
Authorization: Bearer imSXTs2OqSrGWzsFQhIXziFCO3rF...
X-Requested-With: XMLHttpRequest

Odpověď HTTP:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
Date: Wed, 01 Oct 2014 01:41:29 GMT
Content-Length: 27

"Hello, alice@example.com."

Odhlášení ze systému

Vzhledem k tomu, že prohlížeč neukládá přihlašovací údaje do mezipaměti nebo přístupový token, je odhlášení jednoduše otázkou toho, že token zapomenete, odebráním z úložiště relací:

self.logout = function () {
    sessionStorage.removeItem(tokenKey)
}

Principy šablony projektu jednotlivých účtů

Když v šabloně projektu webové aplikace ASP.NET vyberete jednotlivé účty , projekt zahrnuje:

  • Autorizační server OAuth2.
  • Koncový bod webového rozhraní API pro správu uživatelských účtů
  • Model EF pro ukládání uživatelských účtů.

Tady jsou hlavní třídy aplikací, které implementují tyto funkce:

  • AccountController. Poskytuje koncový bod webového rozhraní API pro správu uživatelských účtů. Akce Register je jediná akce, kterou jsme použili v tomto kurzu. Jiné metody ve třídě podporují resetování hesla, přihlášení na sociálních sítích a další funkce.
  • ApplicationUser, definované v /Models/IdentityModels.cs. Tato třída je model EF pro uživatelské účty v databázi členství.
  • ApplicationUserManager, definované v /App_Start/IdentityConfig.cs Tato třída je odvozena od UserManager a provádí operace s uživatelskými účty, jako je například vytvoření nového uživatele, ověření hesel atd. a automaticky zachovává změny v databázi.
  • ApplicationOAuthProvider. Tento objekt se připojí k middlewaru OWIN a zpracovává události vyvolané middlewarem. Odvozuje se od OAuthAuthorizationServerProvider.

Obrázek hlavních tříd aplikací

Konfigurace autorizačního serveru

V StartupAuth.cs nakonfiguruje následující kód autorizační server OAuth2.

PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
    TokenEndpointPath = new PathString("/Token"),
    Provider = new ApplicationOAuthProvider(PublicClientId),
    AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
    // Note: Remove the following line before you deploy to production:
    AllowInsecureHttp = true
};

// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);

Vlastnost TokenEndpointPath je cesta URL ke koncovému bodu autorizačního serveru. To je adresa URL, kterou aplikace používá k získání nosných tokenů.

Vlastnost Provider určuje poskytovatele, který se připojuje k middlewaru OWIN a zpracovává události vyvolané middlewarem.

Tady je základní tok, když aplikace chce získat token:

  1. Pokud chcete získat přístupový token, aplikace odešle požadavek na ~/Token.
  2. Middleware OAuth volá GrantResourceOwnerCredentials poskytovatele.
  3. Zprostředkovatel volá ApplicationUserManager přihlašovací údaje k ověření a vytvoření identity deklarací identity.
  4. Pokud to bude úspěšné, zprostředkovatel vytvoří ověřovací lístek, který se použije k vygenerování tokenu.

Diagram toku autorizace

Middleware OAuth o uživatelských účtech nic neví. Zprostředkovatel komunikuje mezi middlewarem a ASP.NET Identity. Další informace o implementaci autorizačního serveru najdete v tématu OWIN OAuth 2.0 Authorization Server.

Konfigurace webového rozhraní API pro použití nosných tokenů

WebApiConfig.Register V metodě nastaví následující kód ověřování pro kanál webového rozhraní API:

config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

Třída HostAuthenticationFilter umožňuje ověřování pomocí nosných tokenů.

Metoda SuppressDefaultHostAuthentication říká webovému rozhraní API, aby ignorovala veškeré ověřování, ke kterému dojde dříve, než požadavek dosáhne kanálu webového rozhraní API, a to buď službou IIS, nebo middlewarem OWIN. Tímto způsobem můžeme webové rozhraní API omezit na ověřování pouze pomocí nosných tokenů.

Poznámka:

Konkrétně část aplikace MVC může používat ověřování pomocí formulářů, které ukládá přihlašovací údaje do souboru cookie. Ověřování na základě souborů cookie vyžaduje použití anti-forgery tokenů, aby se zabránilo útokům CSRF. To je problém webových rozhraní API, protože webové rozhraní API nemá žádný pohodlný způsob, jak webové rozhraní API odeslat token proti padělání klientovi. (Další informace o tomto problému najdete v tématu Prevence útoků CSRF ve webovém rozhraní API.) Volání SuppressDefaultHostAuthentication zajišťuje, že webové rozhraní API není zranitelné vůči útokům CSRF z přihlašovacích údajů uložených v souborech cookie.

Když klient požádá o chráněný prostředek, stane se to v kanálu webového rozhraní API:

  1. Filtr HostAuthentication volá middleware OAuth k ověření tokenu.
  2. Middleware převede token na identitu deklarací identity.
  3. V tomto okamžiku se požadavek ověří, ale neautorizuje.
  4. Autorizační filtr zkoumá identitu deklarací identity. Pokud deklarace identity autorizuje uživatele pro tento prostředek, je žádost autorizována. Ve výchozím nastavení atribut [Authorize] autorizuje všechny požadavky, které jsou ověřeny. Autorizaci ale můžete provést podle role nebo jiných deklarací identity. Další informace najdete v tématu Ověřování a autorizace ve webovém rozhraní API.
  5. Pokud jsou předchozí kroky úspěšné, řadič vrátí chráněný prostředek. V opačném případě se klientovi zobrazí chyba 401 (Neautorizováno).

Diagram, kdy klient požádá o chráněný prostředek

Další materiály