Een back-end-web-API beveiligen voor multitenant-toepassingen
De toepassing Tailspin Surveys maakt gebruik van een back-end-web-API voor het beheren van CRUD-bewerkingen voor enquêtes. Wanneer een gebruiker bijvoorbeeld op Mijn enquêtes klikt, verzendt de webtoepassing een HTTP-aanvraag naar de web-API:
GET /users/{userId}/surveys
De web-API retourneert een JSON-object:
{
"Published":[],
"Own":[
{"Id":1,"Title":"Survey 1"},
{"Id":3,"Title":"Survey 3"},
],
"Contribute": [{"Id":8,"Title":"My survey"}]
}
De web-API staat geen anonieme aanvragen toe, dus de web-app moet zichzelf verifiëren met OAuth 2 bearer-tokens.
Notitie
Dit is een server-naar-server-scenario. De toepassing maakt geen AJAX-aanroepen naar de API vanaf de browserclient.
Er zijn twee belangrijke benaderingen die u kunt nemen:
- Gedelegeerde gebruikersidentiteit. De webtoepassing wordt geverifieerd met de identiteit van de gebruiker.
- Toepassings-id. De webtoepassing wordt geverifieerd met de client-id, met behulp van de OAuth 2-clientreferentiestroom.
De Tailspin-toepassing implementeert gedelegeerde gebruikersidentiteit. Dit zijn de belangrijkste verschillen:
Gedelegeerde gebruikersidentiteit:
- Het bearer-token dat naar de web-API wordt verzonden, bevat de gebruikersidentiteit.
- De web-API neemt autorisatiebeslissingen op basis van de gebruikersidentiteit.
- De webtoepassing moet 403 (verboden) fouten van de web-API afhandelen als de gebruiker niet is gemachtigd om een actie uit te voeren.
- Normaal gesproken neemt de webtoepassing nog steeds enkele autorisatiebeslissingen die van invloed zijn op de gebruikersinterface, zoals het weergeven of verbergen van UI-elementen).
- De web-API kan mogelijk worden gebruikt door niet-vertrouwde clients, zoals een JavaScript-toepassing of een native clienttoepassing.
Toepassings-id:
- De web-API krijgt geen informatie over de gebruiker.
- De web-API kan geen autorisatie uitvoeren op basis van de gebruikersidentiteit. Alle autorisatiebeslissingen worden genomen door de webtoepassing.
- De web-API kan niet worden gebruikt door een niet-vertrouwde client (JavaScript of native clienttoepassing).
- Deze aanpak is mogelijk iets eenvoudiger te implementeren, omdat er geen autorisatielogica is in de web-API.
In beide benaderingen moet de webtoepassing een toegangs token krijgen. Dit is de referentie die nodig is om de web-API aan te roepen.
- Voor gedelegeerde gebruikersidentiteiten moet het token afkomstig zijn van een id-provider (IDP), zoals Azure Active Directory, die namens de gebruiker een token kan uitgeven.
- Voor clientreferenties kan een toepassing het token van de IDP of een eigen tokenserver hosten. (Maar schrijf geen nieuwe tokenserver; gebruik een goed getest framework zoals [IdentityServer4.)] Als u zich verifieert met Azure AD, wordt het sterk aanbevolen om het toegangsteken op te halen uit Azure AD, zelfs met de clientreferentiestroom.
In de rest van dit artikel wordt ervan uitgenomen dat de toepassing wordt authenticeert met Azure AD.
Een diagram met de webtoepassing die een toegang token aanvraagt van Azure AD en het token naar de web-API stuurt.
De web-API registreren in Azure AD
Om ervoor te zorgen dat Azure AD een Bearer-token voor de web-API kan uitgeven, moet u een aantal dingen configureren in Azure AD.
Registreer de web-API in Azure AD.
Voeg de client-id van de web-app toe aan het web-API-toepassingsmanifest in de
knownClientApplicationseigenschap . Zie de GitHub readme voor meer informatie.Geef de webtoepassing toestemming om de web-API aan te roepen. In de Azure Portal kunt u twee typen machtigingen instellen: 'Toepassingsmachtigingen' voor de toepassings-id (clientreferentiestroom) of 'Gedelegeerde machtigingen' voor gedelegeerde gebruikersidentiteit.
Een schermopname van de Azure Portal met de toepassingsmachtigingen en gedelegeerde machtigingen.
Een toegangs token verkrijgen
Voordat de web-API wordt aangeroepen, haalt de webtoepassing een toegangs token op uit Azure AD. Gebruik in een .NET-toepassing de Microsoft Authentication Library voor .NET (MSAL.NET). Voeg .EnableTokenAcquisitionToCallDownstreamApi() in Startup.cs van de toepassing toe.
Na het ophalen van een token wordt dit door MSAL in de cache opgeslagen. U moet dus ook een implementatie van de tokencache kiezen, die is opgenomen in MSAL. In dit voorbeeld wordt gedistribueerde cache gebruikt. Zie Token caching (Token caching) voor meer informatie.
Het toegang token gebruiken om de web-API aan te roepen
Zodra u het token hebt, roept u een downstream web-API aan. Dit proces wordt beschreven in Een downstream web-API aanroepen met de helperklasse.
Authenticeren in de web-API
De web-API moet het bearer-token verifiëren. In ASP.NET Core kunt u het pakket Microsoft.AspNet.Authentication.JwtBearer gebruiken. Dit pakket biedt middleware waarmee de toepassing OpenID-Verbinding maken bearer-tokens kan ontvangen.
Registreer de middleware in uw Startup web-API-klasse.
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(jtwOptions =>
{
jtwOptions.Events = new SurveysJwtBearerEvents(loggerFactory.CreateLogger<SurveysJwtBearerEvents>());
},
msIdentityOptions => {
Configuration.GetSection("AzureAd").Bind(msIdentityOptions);
});
Gebeurtenissen is een klasse die is afgeleid van JwtBearerEvents.
Validatie van vergever
Valideer de tokenuitgever in de gebeurtenis JwtBearerEvents.TokenValidated. De vergever wordt verzonden in de iss-claim.
In de toepassing Surveys verwerkt de web-API de [aanmelding van tenants niet.] Daarom wordt alleen gecontroleerd of de vergever al in de toepassingsdatabase staat. Zo niet, dan wordt er een uitzondering veroorzaakt, waardoor de verificatie mislukt.
public override async Task TokenValidated(TokenValidatedContext context)
{
var principal = context.Ticket.Principal;
var tenantManager = context.HttpContext.RequestServices.GetService<TenantManager>();
var userManager = context.HttpContext.RequestServices.GetService<UserManager>();
var issuerValue = principal.GetIssuerValue();
var tenant = await tenantManager.FindByIssuerValueAsync(issuerValue);
if (tenant == null)
{
// The caller was not from a trusted issuer. Throw to block the authentication flow.
throw new SecurityTokenValidationException();
}
var identity = principal.Identities.First();
// Add new claim for survey_userid
var registeredUser = await userManager.FindByObjectIdentifier(principal.GetObjectIdentifierValue());
identity.AddClaim(new Claim(SurveyClaimTypes.SurveyUserIdClaimType, registeredUser.Id.ToString()));
identity.AddClaim(new Claim(SurveyClaimTypes.SurveyTenantIdClaimType, registeredUser.TenantId.ToString()));
// Add new claim for Email
var email = principal.FindFirst(ClaimTypes.Upn)?.Value;
if (!string.IsNullOrWhiteSpace(email))
{
identity.AddClaim(new Claim(ClaimTypes.Email, email));
}
}
Zoals dit voorbeeld laat zien, kunt u ook de gebeurtenis TokenValidated gebruiken om de claims te wijzigen. Houd er wel voor dat de claims rechtstreeks afkomstig zijn van Azure AD. Als de webtoepassing de claims wijzigt die worden ontvangen, worden deze wijzigingen niet doorgevoerd in het bearer-token dat de web-API ontvangt. Zie Claimtransformaties voor meer informatie.
Autorisatie
Zie Autorisatie op basis van rollen en resources voor een algemene bespreking van autorisatie.
De JwtBearer-middleware verwerkt de autorisatie-antwoorden. Als u bijvoorbeeld een controlleractie wilt beperken tot geverifieerde gebruikers, gebruikt u het kenmerk [Autoriseren] en geeft u JwtBearerDefaults.AuthenticationScheme op als verificatieschema:
[Authorize(ActiveAuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
Dit retourneert een 401-statuscode als de gebruiker niet is geverifieerd.
Als u een controlleractie wilt beperken op autorisatiebeleid, geeft u de naam van het beleid op in het kenmerk [Autorisatie] :
[Authorize(Policy = PolicyNames.RequireSurveyCreator)]
Hiermee wordt een 401-statuscode als de gebruiker niet is geverifieerd, en 403 als de gebruiker is geverifieerd maar niet geautoriseerd. Registreer het beleid bij het opstarten:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(options =>
{
options.AddPolicy(PolicyNames.RequireSurveyCreator,
policy =>
{
policy.AddRequirements(new SurveyCreatorRequirement());
policy.RequireAuthenticatedUser(); // Adds DenyAnonymousAuthorizationRequirement
policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
});
options.AddPolicy(PolicyNames.RequireSurveyAdmin,
policy =>
{
policy.AddRequirements(new SurveyAdminRequirement());
policy.RequireAuthenticatedUser(); // Adds DenyAnonymousAuthorizationRequirement
policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
});
});
// ...
}
Toepassingsgeheimen beveiligen
Het is gebruikelijk om toepassingsinstellingen te hebben die gevoelig zijn en moeten worden beveiligd, zoals:
- Databaseverbindingsreeksen
- Wachtwoorden
- Cryptografische sleutels
Als beveiligingsrisico best practice moet u deze geheimen nooit opslaan in broncodebeheer. Het is te eenvoudig om ze te laten — lekken, zelfs als uw opslagplaats voor broncode privé is. En het gaat niet alleen om het bewaren van geheimen voor het algemene publiek. Bij grotere projecten wilt u mogelijk beperken welke ontwikkelaars en operators toegang hebben tot de productiegeheimen. (Instellingen voor test- of ontwikkelomgevingen zijn anders.)
Een veiligere optie is om deze geheimen op te slaan in Azure Key Vault. Key Vault is een cloudservice voor het beheren van cryptografische sleutels en andere geheimen. In dit artikel wordt beschreven hoe u Key Vault voor het opslaan van configuratie-instellingen voor uw app.
In de toepassing Tailspin Surveys zijn de volgende instellingen geheim:
- De database connection string.
- De Redis-connection string.
- Het clientgeheim voor de webtoepassing.
De toepassing Surveys laadt configuratie-instellingen van de volgende locaties:
- Het bestand appsettings.json
- Het gegevensopslag voor gebruikersgeheimen (alleen de ontwikkelomgeving; voor testen)
- De hostingomgeving (app-instellingen in Azure-web-apps)
- Key Vault (indien ingeschakeld)
Elk van deze overschrijvingen de vorige, zodat alle instellingen die zijn opgeslagen in Key Vault prioriteit hebben.
Notitie
Standaard is de Key Vault-configuratieprovider uitgeschakeld. Dit is niet nodig voor het lokaal uitvoeren van de toepassing. U zou deze inschakelen in een productie-implementatie.
Bij het opstarten leest de toepassing instellingen van elke geregistreerde configuratieprovider en gebruikt deze om een sterk getypeerd optiesobject te vullen. Zie Opties en configuratieobjecten gebruiken voor meer informatie.