ASP.NET Core 2 Security - WordPress.com · .NET 1.0 ASP.NET 1.0 2006.NET 3.0 WCF (#fail) 2009 WIF...

Preview:

Citation preview

ASP.NET Core & MVC 2Security Overview

Dominick Baier@leastprivilege

2@leastprivilege

Me

• Independent Consultant – Specializing on Application Security Architectures

– Working with Software Development Teams (ISVs and in-house)

• Creator and Maintainer of IdentityServer OSS Project– Certified OpenID Connect & OAuth 2.0 Implementation for .NET

– https://identityserver.io

email dominick.baier@leastprivilege.comblog http://leastprivilege.comtwitter @leastprivilegeslides https://speakerdeck.com/leastprivilege

Modern Application Architecture

Browser

Native App

Server App/Thing

Web App

Service Service

Service

Identity

4@leastprivilege

.NET Timeline

2002

.NET 1.0ASP.NET 1.0

2006

.NET 3.0WCF (#fail)

2009

WIF

2012

.NET 4.5

2013

Katana 2.0

2014

Katana 3.0

2016

.NET CoreASP.NET Core

v1

Claims+ Security Token Support= External Authentication

2017

.NET CoreASP.NET Core

v2

5@leastprivilege

The new .NET

6@leastprivilege

ASP.NET Core Architecture• ASP.NET Core is the HTTP runtime • MVC is Microsoft's primary application framework– combines web UI & API

Console Application

.NET (Core)

ASP.NET Core

Middleware MiddlewareUser Agent MVC

DI

7@leastprivilege

ASP.NET Core Security on a single Slide• Everything is based on ClaimsPrincipal

– no more custom IPrincipal

• It's all about services– authentication– authorization– claims transformation– data protection– anti-forgery– CORS– encoding

• Middleware for automatic per-request invocation

8@leastprivilege

Authentication in ASP.NET Core

• Handlers in DI implement specific authentication methods– Cookies for browser based authentication– Google, Facebook, and other social authentication*– OpenId Connect for external authentication– JSON web token (JWT) for token-based authentication

• Middleware invokes handlers for request related processing– handlers can be also invoked manually

* 20+ more https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers

9@leastprivilege

Interacting with the authentication system

• Extension methods on HttpContext call the IAuthenticationService in DI

public static class AuthenticationHttpContextExtensions{

public static Task SignInAsync(this HttpContext context, ClaimsPrincipal principal) { }public static Task SignInAsync(this HttpContext context, string scheme, ClaimsPrincipal principal) { }

public static Task SignOutAsync(this HttpContext context) { }public static Task SignOutAsync(this HttpContext context, string scheme) { }

public static Task ChallengeAsync(this HttpContext context) { }public static Task ChallengeAsync(this HttpContext context, string scheme) { }

public static Task ForbidAsync(this HttpContext context) { }public static Task ForbidAsync(this HttpContext context, string scheme) { }

public static Task<AuthenticateResult> AuthenticateAsync(this HttpContext context) { }public static Task<AuthenticateResult> AuthenticateAsync(this HttpContext context, string scheme) { }

}

10@leastprivilege

Setting up authentication

• Global settings go into DI– e.g. default schemes

• Authentication middleware invokes handlers

public void ConfigureServices(IServiceCollection services){

services.AddAuthentication();}

public void Configure(IApplicationBuilder app){

app.UseAuthentication();}

11@leastprivilege

Cookie Authentication

public void ConfigureServices(IServiceCollection services){

services.AddAuthentication(defaultScheme: "Cookies").AddCookie("Cookies", options =>{

options.LoginPath = "/account/login";options.AccessDeniedPath = "/account/denied";

options.Cookie.Name = "myapp";options.Cookie.Expiration = TimeSpan.FromHours(8);options.SlidingExpiration = false;

});}

12@leastprivilege

Cookies: Logging in• SignInAsync issues cookie

– either using a named scheme, or default

var claims = new Claim[]{

new Claim("sub", "37734"),new Claim("name", "Brock Allen")

};

var ci = new ClaimsIdentity(claims, "password", "name", "role");var cp = new ClaimsPrincipal(ci);

await HttpContext.SignInAsync(cp);

13@leastprivilege

Cookies: Logging out• SignOutAsync removes cookie

await HttpContext.SignOutAsync();

14@leastprivilege

Data Protection

• Who thought this would be a good idea??

For giggles: "https://www.google.com/#q=<machineKey filetype:config"

<system.web><!– copied from http://machinekeys.ru seemed legit --><machineKey decryptionKey="656E7...617365206865726547A5"

validationKey="07C1493415E4405F08...6EF8B1F" /> </system.web>

15@leastprivilege

Data Protection

• Used to protect cookies and other secrets– IDataProtectionProvider in DI

• Uses a key container file– stored outside of application directory*– uses a key ring with automatic rotation– keys should be protected

• Needs to be synchronized between nodes in a farm

* https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/default-settings

16@leastprivilege

Claims Transformation

• Per-request manipulation of principal & claims– register an instance of IClaimsTransformation in DI– gets called from the handler's AuthenticateAsync method

public class ClaimsTransformer : IClaimsTransformation{

public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal){

return await CreateApplicationPrincipalAsync(principal);}

}

services.AddTransient<IClaimsTransformation, ClaimsTransformer>();

17@leastprivilege

Authorization

• Complete re-write– better separation of business code and authorization logic– policy based authorization– resource/action based authorization– DI enabled

ASP.NET 4.x version: https://github.com/DavidParks8/Owin-Authorization

18@leastprivilege

[Authorize]

• Similar syntax– roles still supported

[Authorize]public class HomeController : Controller{

[AllowAnonymous]public IActionResult Index(){

return View();}

[Authorize(Roles = "Sales")]public IActionResult About(){

return View(User);}

}

19@leastprivilege

Authorization policies

services.AddAuthorization(options =>{

options.AddPolicy("ManageCustomers", policy =>{

policy.RequireAuthenticatedUser();policy.RequireClaim("department", "sales");policy.RequireClaim("status", "senior");

});});

[Authorize("ManageCustomers")]public IActionResult Manage(){

// stuff}

Startup

Controller

20@leastprivilege

Programmatically using policiespublic class CustomerController : Controller{

private readonly IAuthorizationService _authz;

public CustomerController(IAuthorizationService authz){

_authz = authz;}

public async Task<IActionResult> Manage(){

var result = await _authz.AuthorizeAsync(User, "ManageCustomers");if (result.Succeeded) return View();

return Forbid();}

}

21@leastprivilege

…or from a View

@using Microsoft.AspNetCore.Authorization@inject IAuthorizationService _authz

@if ((await _authz.AuthorizeAsync(User, "ManageCustomers")).Succeeded){

<div><a href="/customers/test">Manage</a>

</div>}

22@leastprivilege

Custom Requirementspublic class JobLevelRequirement : IAuthorizationRequirement

{

public JobLevel Level { get; }

public JobLevelRequirement(JobLevel level)

{

Level = level;

}

}

public static class StatusPolicyBuilderExtensions

{

public static AuthorizationPolicyBuilder RequireJobLevel(

this AuthorizationPolicyBuilder builder, JobLevel level)

{

builder.AddRequirements(new JobLevelRequirement(level));

return builder;

}

}

23@leastprivilege

Handling Requirementspublic class JobLevelRequirementHandler : AuthorizationHandler<JobLevelRequirement>{

private readonly IOrganizationService _service;

public JobLevelRequirementHandler(IOrganizationService service){

_service = service;}

protected override void Handle(AuthorizationContext context, JobLevelRequirement requirement)

{var currentLevel = _service.GetJobLevel(context.User);

if (currentLevel == requirement.Level){

context.Succeed(requirement);}

}}

24@leastprivilege

Resource-based Authorization

Subject ObjectOperation

- client ID- subject ID- scopes

- more claims

+ DI

- read- write- send via email- ...

- ID- owner

- more properties

+ DI

25@leastprivilege

Example: Document resource

public class DocumentAuthorizationHandler :AuthorizationHandler<OperationAuthorizationRequirement, Document>

{public override Task HandleRequirementAsync(AuthorizationHandlerContext context,OperationAuthorizationRequirement operation,Document resource)

{// authorization logic

}}

services.AddTransient<IAuthorizationHandler, DocumentAuthorizationHandler>();Add handler in DI:

26@leastprivilege

Invoking the authorization handlerpublic class DocumentController : Controller{

private readonly IAuthorizationService _authz;

public DocumentController(IAuthorizationService authz){

_authz = authz;}

public async Task<IActionResult> Update(Document doc){

if ((await _authz.AuthorizeAsync(User, doc, Operations.Update)).Failure){

return Forbid();}

// do stuff}

}

27@leastprivilege

Summary: Cookies & AuthorizationBrowser sends

request

Authentication middleware checks DefaultAuthenticate

scheme

Default authenticate handler calls

AuthenticateAsync

Cookie found?

PopulateHttpContext.User

Request arrives at Controller

[Authorize]?

Executeaction method

Get current user or call handler based

on specified scheme

AuthZ filter calls Challenge and

redirects to LoginPath

Is userauthenticated?

Account controller authenticates user and redirects back

Is userauthorized?

AuthZ filter calls Forbid and redirects to

AccessDeniedPath

yes

no

yes

no

no

yes

no

yes

28@leastprivilege

External Authentication

• ASP.NET Core supports– Google, Twitter, Facebook, Microsoft Account– OpenID Connect & JSON Web Tokens

• New generic OAuth 2.0 handler makes integration with other proprietary providers easier– LinkedIn, Slack, Spotify, WordPress, Yahoo, Github, Instragram, BattleNet,

Dropbox, Paypal, Vimeo…

https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers

29@leastprivilege

Social Identity Providers

• Enabled with AddGoogle, et al.– Rely upon cookie authentication handler

services.AddAuthentication("Cookies").AddCookie("Cookies", options =>{

options.LoginPath = "/account/login";options.AccessDeniedPath = "/account/denied";

}).AddGoogle("Google", options =>{

options.SignInScheme = "Cookies";

options.ClientId = "...";options.ClientSecret = "...";

});

30@leastprivilege

Social Identity Providers

• Challenge triggers redirect for login– Control URL user returns to and state with AuthenticationProperties– MVC ChallengeResult works with action result architecture

var props = new AuthenticationProperties{

RedirectUri = "/Home/Secure"};await HttpContext.ChallengeAsync("Google", props);

// or if using MVC:

return Challenge("Google", props);

31@leastprivilege

Summary: External Authentication

Challenge(scheme)

Handler'sChallengeAsync

method getsinvoked

Redirect to externalprovider

External provider

Authentication middleware asks

handlers who wantsto process request

callback Handlers does theprotocol post-

processing

Call sign-in handler(sets cookie

containing externalidentity)

Redirect to final URL

32@leastprivilege

Mixing local and external Authentication

• Typically need provisioning logic for users from social providers– Use additional cookie handler for processing registration

services.AddAuthentication("Cookies").AddCookie("Cookies", options =>{

options.LoginPath = "/account/login";options.AccessDeniedPath = "/account/denied";

}).AddCookie("Temp").AddGoogle("Google", options =>{

options.SignInScheme = "Temp";

options.ClientId = "...";options.ClientSecret = "...";

});

33@leastprivilege

Mixing local and external Authentication

• Redirect page performs local account registration logic– AuthenticateAsync triggers cookie middleware– Create local account or load existing account– Use primary cookie middleware to log user in (and remove temp cookie)

var tempUser = (await HttpContext.AuthenticateAsync("Temp")).Principal;var userIdClaim = tempUser.FindFirst(ClaimTypes.NameIdentifier);var provider = userIdClaim.Issuer;var userId = userIdClaim.Value;

// create local account if new, or load existing local account

var user = new ClaimsPrincipal(...);await HttpContext.SignInAsync(user);await HttpContext.SignOutAsync("Temp");

34@leastprivilege

Summary: External Authentication with Callback

Challenge(scheme)

Handler'sChallengeAsync

method getsinvoked

Redirect to externalprovider

External provider

Authentication middleware asks

handlers who wantsto process request

callback Handlers does theprotocol post-

processing

Call sign-in handler(sets temporary

cookie containingexternal identity)

Run app-level post-processing

SignOut temp cookie

SignIn primary cookie

Redirect to final URL

35@leastprivilege

The way forward…

Browser

Native App

Server App"Thing"

Web App

Web API Web API

Web API

Security Token Service

Authentication, SSO, account linking, federation, social logins…

36@leastprivilege

Security Protocols

Browser

Native App

Server App"Thing"

Web App

Web API Web API

Web APIOpenID Connect

OAuth 2.0

OAuth 2.0

OAuth 2.0

OAuth 2.0

OAuth 2.0

OAuth 2.0

Security Token ServiceOpenID Connect

OpenID Connect

37@leastprivilege

http://openid.net/connect/

38@leastprivilege

OpenID Connect handler

services.AddAuthentication(options =>{

options.DefaultScheme = "Cookies";options.DefaultChallengeScheme = "oidc";

}.AddCookie("Cookies").AddOpenIdConnect("oidc", options =>{

options.Authority = "https://demo.identityserver.io";options.ClientId = "mvc";

});

39@leastprivilege

Access Token Validation

• JWT bearer token authentication handler

public void ConfigureServices(IServiceCollection services){

services.AddAuthentication("Bearer").AddJwtBearer("Bearer", options =>{

options.Authority = "https://your_oidc_provider";options.Audience = "your_api_identifier";

});}

40@leastprivilege

Issuing Tokens

https://docs.microsoft.com/en-us/aspnet/core/security/authentication/community

41@leastprivilege

Certified OpenID ConnectProvider

42@leastprivilege

43@leastprivilege

Resources• https://github.com/leastprivilege/

– AspNetCoreSecuritySamples

• https://github.com/dotnet– corefx– coreclr

• https://github.com/aspnet– home– security– announcements

• https://docs.asp.net• https://identityserver.io

44@leastprivilege

thank you!

Recommended