Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Part 2 - Beyond OAuth 2.1

Part 2 - Beyond OAuth 2.1

NDC 2020

Dominick Baier

June 10, 2020
Tweet

More Decks by Dominick Baier

Other Decks in Programming

Transcript

  1. OAuth 2.1 & Beyond
    Dominick Baier
    @leastprivilege

    View Slide

  2. 2
    @leastprivilege
    Me
    • Independent Consultant
    – Specializing on Application Security Architectures & Security Protocols
    – Working with Software Development Teams (ISVs and in-house)
    • Co-Creator of IdentityServer & IdentityModel OSS Project
    – Certified OpenID Connect & OAuth 2.0 Engine for ASP.NET Core
    – https://identityserver.io
    • Co-Creator of PolicyServer
    – Authorization for modern Applications
    – https://policyserver.io
    email [email protected]
    blog https://leastprivilege.com
    twitter @leastprivilege
    slides https://speakerdeck.com/leastprivilege

    View Slide

  3. 3
    @leastprivilege
    High Security OAuth
    FAPI
    https://fapi.openid.net

    View Slide

  4. 4
    @leastprivilege
    Agenda
    • OAuth security best current practices & OAuth 2.1
    – common attacks, countermeasures & recommendations
    • Advanced OAuth
    – tokens, scopes, resources and audience restrictions
    – rich authorization requests (RAR)
    – JWT secured authorization requests & request objects (JAR)
    – pushed authorization requests (PAR)
    – strong client authentication
    – proof-of-possession access tokens
    – delegation, impersonation and token exchange
    – "OAuth 3.0" outlook
    https://identityserver.io/training/advanced-oauth.html

    View Slide

  5. 5
    @leastprivilege
    Agenda
    • OAuth 2.0 to 2.1 diff
    • Beyond
    – improving front-channel security
    – improving back-channel security
    – proof-of-possession access tokens

    View Slide

  6. 6
    @leastprivilege
    Relevant Documents
    OAuth 2.0 Security
    Best Current Practice
    https://tools.ietf.org/html/draft-ietf-oauth-
    security-topics/
    OAuth 2.0 Threat Model &
    Security Considerations
    https://tools.ietf.org/html/rfc6819
    JSON Web Token
    Best Current Practices
    https://tools.ietf.org/html/rfc8725
    OAuth 2.0 for native Applications
    https://tools.ietf.org/html/rfc8252
    OAuth 2.0 for Browser-Based Applications
    https://tools.ietf.org/wg/oauth/draft-ietf-oauth-browser-based-apps/
    JSON Web Token (JWT) Profile
    for OAuth 2.0 Access Tokens
    https://tools.ietf.org/wg/oauth/draft-ietf-oauth-
    access-token-jwt/

    View Slide

  7. 7
    @leastprivilege
    OAuth 2.1
    • RFC 6749 + RFC 6750 + various BCPs == OAuth 2.1
    – https://tools.ietf.org/html/draft-parecki-oauth-v2-1
    • Most important changes
    – omission of implicit grant
    – omission of password grant
    – PKCE must be used with the authorization code grant
    – redirect URIs must be compared using exact string matching
    – refresh tokens must be either sender-constrained or one-time use only
    – bearer tokens usage omits token in the query string

    View Slide

  8. 8
    @leastprivilege
    Improved Front-Channel with
    RAR, JAR & PAR

    View Slide

  9. 9
    @leastprivilege
    Rich Authorization Requests (RAR)
    • scope parameter is typically coarse grained
    – some implementations use custom structured format, e.g. transaction:id
    • RAR introduces new authorization_detail parameter
    – "allow client to make a payment of 45€"
    – "allow client to read folder X, and write file Y"
    • Outcome of decision can be used to restrict access token to certain action(s)
    – typically combined with consent
    https://tools.ietf.org/html/draft-ietf-oauth-rar

    View Slide

  10. 10
    @leastprivilege
    Example
    [
    {
    "type": "customer_information",
    "locations": [ "https://example.com/customers" ],
    "actions": [ "read" ],
    "datatypes": [ "contacts" ]
    },
    {
    "type": "customer_images",
    "locations": [ "https://example.com/images" ],
    "actions": [ "print" ],
    "datatypes": [ "photos" ],
    "identifier": "1"
    }
    ]
    represents the kinds of
    data being requested
    from the resource
    A string identifier
    indicating a specific
    resource available
    at the API
    schema

    View Slide

  11. 11
    @leastprivilege
    Example (2)
    {
    "type": "https://scheme.example.org/files",
    "locations": [ "https://example.com/files" ],
    "permissions": [
    {
    "path": "/myfiles/A",
    "access": [ "read" ]
    },
    {
    "path": "/myfiles/A/X",
    "access": [ "read", "write" ]
    }
    ]
    }

    View Slide

  12. 12
    @leastprivilege
    Example Authorization Request
    GET /authorize?response_type=code
    &client_id=s6BhdRkqt3
    &state=af0ifjsldkj
    &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
    &code_challenge_method=S256
    &code_challenge=K2-ltc83acc4h0c9w6ESC_rEMTJ3bwc-uCHaoeK1t8U
    &authorization_details=%5B%7B%22type%22%3A%22account%5Finformati
    on%22%2C%22actions%22%3A%5B%22list%5Faccounts%22%2C%22read%5Fbal
    ances%22%2C%22read%5Ftransactions%22%5D%2C%22locations%22%3A%5B%
    22https%3A%2F%2Fexample%2Ecom%2Faccounts%22%5D%7D%5D HTTP/1.1
    Host: server.example.com

    View Slide

  13. 13
    @leastprivilege
    Access Token
    • Included either in JWT or introspection response
    {
    "iss": "http://as.com",
    "aud": "https://example.com/images"
    "iat": 1588420700
    "exp": 1588420800,
    "sub": "123",
    "client_id": "456"
    "authorization_details": [ … ]
    }

    View Slide

  14. 14
    @leastprivilege
    JWT Secured Authorization Requests (JAR)
    • Allows passing authorization parameters as a JSON Web Token
    – signed for integrity and client authentication
    – encrypted for confidentiality
    • Mitigates several known attacks
    – redirection URI re-writing
    – mix-up attacks
    https://tools.ietf.org/html/draft-ietf-oauth-jwsreq

    View Slide

  15. 15
    @leastprivilege
    JWT Secured Authorization Request
    GET /authorize?client_id=client&response_type=code&redirect_uri=https://myapp.com/cb
    &state=abc&code_challenge=def&scope=openid customer.api
    GET /authorize?client_id=client&request=
    eyJhbGciOiJSUzI1NiIsImtpZCI6ImsyYmRjIn0.ewogICAgImlzcyI6ICJzNkJoZF
    JrcXQzIiwKICAgIC.JhdWQiOiAiaHR0cHM6Ly9zZXJ2ZXIuZXhhbXBsZS5jb20iL
    Aog ICAgInJlc3BvbnNlX3R5cGUiOiAiY29kZSBpZF90b2tlbiIsCiAgICAiY2xpZW
    50X2 lkIjogInM2QmhkUmtxdDMiLAogICAgInJlZGlyZWN0X3VyaSI6ICJodHR
    wczovL2Ns aWVudC5leGFtcGxlLm9yZy9jYiIsCiAgICAic2NvcGUiOiAib3Blbml
    kIiwKICAgIC JzdGF0ZSI6ICJhZjBpZmpzbGRraiI.sCiAgICAibm9uY2UiOiAibi0wU
    zZfV3pBMk1q IiwKICAgICJtYXhfYWdlIjogODY0MDAKfQ.Nsxa_18VUElVaPjqW
    _ToI1yrEJ67BgK b5xsuZRVqzGkfKrOIX7BCx0biSxYGmjK9KJPctH1OC0iQJwXu5Y

    View Slide

  16. 16
    @leastprivilege
    Request Object
    {
    "typ": "oauth.authz.req+jwt",
    "alg": "RS256",
    "kid": "1"
    }.
    {
    "iss": "client",
    "aud": "https://authorizationserver.com",
    "response_type": "code",
    "client_id": "client",
    "redirect_uri": "https://myapp.com/cb",
    "scope": "openid customer.api",
    "state": "abc",
    "code_challenge": "def"
    }.
    [Signature]

    View Slide

  17. 17
    @leastprivilege
    Enabling JAR in IdentityServer
    new Client
    {
    ClientId = "jar.client",
    AllowedGrantTypes = GrantTypes.Code,
    RequireRequestObject = true,
    ClientSecrets =
    {
    new Secret
    {
    Type = IdentityServerConstants.SecretTypes.JsonWebKey,
    Value = "{'e':'AQAB','kid':'Zz..EA','kty':'RSA','n':'wW..Kw'}"
    }
    }
    }

    View Slide

  18. 18
    @leastprivilege
    Pushed Authorization Requests (PAR)
    • Complements JAR
    • Provides interoperable endpoint to push authorization request parameters
    – in exchange for a request_uri
    – allows for authentication for confidential clients
    – manages entropy and lifetime
    https://tools.ietf.org/html/draft-ietf-oauth-par

    View Slide

  19. 19
    @leastprivilege
    Pushed Authorization Request & Response
    POST /par
    client_id=client&
    response_type=code&
    redirect_uri=https://myapp.com/cb&
    state=abc&
    code_challenge=def&
    authorization_details={…}
    HTTP/1.1 201 Created
    {
    "request_uri": "urn:par:bwc4JK-cc191e-Y1LTC2",
    "expires_in": 90
    }

    View Slide

  20. 20
    @leastprivilege
    Authorization Request using request_uri
    • Front-channel only used to transmit reference to authenticated and
    validated request object
    GET /authorize?client_id=client&
    request_uri=urn:par:bwc4JK-cc191e-Y1LTC2

    View Slide

  21. 21
    @leastprivilege
    Summary
    • RAR allows for structured resource access
    – replaces scope
    • JAR allows for signed, authenticated authorization requests
    – also encrypted if needed
    – also allows transmitting additional (trusted) data
    • PAR provides endpoint to push authorization request
    – removes all data from front-channel

    View Slide

  22. 22
    @leastprivilege
    Improved Back-Channel with
    stronger Authentication and PoP

    View Slide

  23. 23
    @leastprivilege
    Shared Secrets
    POST /token
    grant_type=client_credentials
    client_id=client
    client_secret=secret

    View Slide

  24. 24
    @leastprivilege
    Recommendations
    • Use client secrets based on asymmetric keys
    – AS does not need to store any secret (only the public key)
    – secret not exposed over the wire
    • private_key_jwt
    – https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication
    – https://tools.ietf.org/html/rfc7523
    • Mutual TLS
    – https://tools.ietf.org/html/rfc8705

    View Slide

  25. 25
    @leastprivilege
    private_key_jwt
    • Client creates a JWT and signs it with its private key
    – can add additional custom claims
    Claim Description
    iss must contain the client ID of the OAuth client
    sub must contain the client ID of the OAuth client
    aud identifies the AS, e.g. URL of token endpoint or issuer name
    jti unique identifier for the token, which can be used to
    prevent reuse of the token
    exp expiration time of JWT
    iat time at which the jwt was issued (optional)

    View Slide

  26. 26
    @leastprivilege
    Sending a private_key_jwt
    • Assertion type
    – urn:ietf:params:oauth:client-assertion-type:jwt-bearer
    POST /token
    grant_type=client_credentials
    client_assertion_type=urn:…
    client_assertion=

    View Slide

  27. 27
    @leastprivilege
    Creating a Client JWT
    private static string CreateClientToken(SigningCredentials credential, string clientId, string audience)
    {
    var now = DateTime.UtcNow;
    var token = new JwtSecurityToken(
    clientId,
    audience,
    new List()
    {
    new Claim(JwtClaimTypes.JwtId, Guid.NewGuid().ToString()),
    new Claim(JwtClaimTypes.Subject, clientId),
    new Claim(JwtClaimTypes.IssuedAt, now.ToEpochTime().ToString(), ClaimValueTypes.Integer64)
    },
    now,
    now.AddMinutes(1),
    credential
    );
    var tokenHandler = new JwtSecurityTokenHandler();
    return tokenHandler.WriteToken(token);
    }

    View Slide

  28. 28
    @leastprivilege
    Sending a private_key_jwt
    static async Task RequestTokenAsync(SigningCredentials credential)
    {
    var client = new HttpClient();
    var clientToken = CreateClientToken(credential, "client.jwt", disco.TokenEndpoint);
    var response = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
    {
    Address = disco.TokenEndpoint,
    Scope = "api1",
    ClientAssertion =
    {
    Type = OidcConstants.ClientAssertionTypes.JwtBearer,
    Value = clientToken
    }
    });
    if (response.IsError) throw new Exception(response.Error);
    return response;
    }

    View Slide

  29. 29
    @leastprivilege
    private_key_jwt and ASP.NET Core Clients
    public class OidcEvents : OpenIdConnectEvents
    {
    private readonly ClientTokenService _clientTokenService;
    public OidcEvents(ClientTokenService clientTokenService)
    {
    _clientTokenService = clientTokenService;
    }
    public override Task AuthorizationCodeReceived(AuthorizationCodeReceivedContext context)
    {
    context.TokenEndpointRequest.ClientAssertionType =
    OidcConstants.ClientAssertionTypes.JwtBearer;
    context.TokenEndpointRequest.ClientAssertion = _clientTokenService.CreateClientToken();
    return Task.CompletedTask;
    }
    }

    View Slide

  30. 30
    @leastprivilege
    Enabling private_key_jwt in IdentityServer
    • Wires up parser and validator
    • Adds replay cache based on IDistributedCache
    – can be replaced
    var builder = services.AddIdentityServer()
    .AddJwtBearerClientAuthentication();

    View Slide

  31. 31
    @leastprivilege
    Client Definition in IdentityServer
    new Client
    {
    ClientId = "private.key.jwt",
    AllowedGrantTypes = GrantTypes.CodeAndClientCredentials,
    RequirePkce = true,
    ClientSecrets =
    {
    new Secret
    {
    Type = IdentityServerConstants.SecretTypes.X509CertificateBase64,
    Value = "M..A="
    },
    new Secret
    {
    Type = IdentityServerConstants.SecretTypes.JsonWebKey,
    Value = "{'e':'AQAB','kid':'Zz..EA','kty':'RSA','n':'wW..Kw'}"
    }
    }
    }

    View Slide

  32. 32
    @leastprivilege
    "Proof-of-Possession" History
    • Most wanted feature since initial OAuth 2.0 spec
    – only bearer tokens were specified
    • Several attempts to introduce PoP at the application layer
    – required a lot of application layer crypto (e.g. signing of HTTP requests)
    – some unsolved problems (e.g. streaming)
    • ...then at the transport layer
    – HTTP token binding (deprecated)

    View Slide

  33. 33
    @leastprivilege
    Weakness of Bearer Tokens
    • Bearer tokens are not bound to the client
    – attacker can replay leaked tokens
    Authorization: Bearer

    View Slide

  34. 34
    @leastprivilege
    Proof of Possession using MTLS
    • OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access
    Tokens
    – https://tools.ietf.org/html/rfc8705
    • Covers both authentication and Proof-of-Possession

    View Slide

  35. 35
    @leastprivilege
    Mutual TLS
    TLS Tunnel
    server certificate
    client certificate
    • authentication
    • proving to know a secret
    (private key)
    • validate chain of trust
    (optional)
    • negotiate key material
    • sign & encrypt traffic

    View Slide

  36. 36
    @leastprivilege
    Sender Constrained Access Tokens w/ MTLS
    {
    "iss": "https://issuer",
    "exp": 1340819380,
    "nbf": 1340818761,
    "client_id": "182jmm199",
    "scope": "api1",
    "cnf": {
    "x5t#S256": "bwcK0esc3ACC3DB2Y5_lESsXE8o9ltc05O89jdN-dg2"
    }
    }

    View Slide

  37. 37
    @leastprivilege
    Creating an X.509 Client Certificate
    static X509Certificate2 CreateClientCertificate(string name)
    {
    var distinguishedName = new X500DistinguishedName($"CN={name}");
    using (var rsa = RSA.Create(2048))
    {
    var request = new CertificateRequest(
    distinguishedName, rsa, HashAlgorithmName.SHA256,RSASignaturePadding.Pkcs1);
    request.CertificateExtensions.Add(
    new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment |
    X509KeyUsageFlags.DigitalSignature, false));
    request.CertificateExtensions.Add(
    new X509EnhancedKeyUsageExtension(
    new OidCollection { new Oid("1.3.6.1.5.5.7.3.2") }, false));
    return request.CreateSelfSigned(
    new DateTimeOffset(DateTime.UtcNow.AddDays(-1)),
    new DateTimeOffset(DateTime.UtcNow.AddDays(10)));
    }
    }

    View Slide

  38. 38
    @leastprivilege
    Setting a Client Certificate
    static SocketsHttpHandler GetHandler(X509Certificate2 certificate)
    {
    var handler = new SocketsHttpHandler();
    handler.SslOptions.ClientCertificates =
    new X509CertificateCollection { certificate };
    return handler;
    }

    View Slide

  39. 39
    @leastprivilege
    Calling the Token Endpoint
    static async Task RequestTokenAsync()
    {
    var client = new HttpClient(GetHandler());
    var disco = await client.GetDiscoveryDocumentAsync("https://identityserver.local");
    var endpoint = disco
    .TryGetValue(OidcConstants.Discovery.MtlsEndpointAliases)
    .Value(OidcConstants.Discovery.TokenEndpoint)
    .ToString();
    var response = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
    {
    Address = endpoint,
    ClientId = "mtls",
    Scope = "api1"
    });
    return response;
    }

    View Slide

  40. 40
    @leastprivilege
    MTLS Endpoints
    • Sub-path
    • Sub-domain
    • Separate domain
    https://identityserver.io/connect/mtls/*
    https://mtls.identityserver.io/*
    https://identityserver-mtls.io/*

    View Slide

  41. 41
    @leastprivilege
    Server Metadata
    • Endpoint aliases specify the URLs of the MTLS versions of the standard
    endpoints

    View Slide

  42. 42
    @leastprivilege
    Choice of Web Server / Proxy
    • IIS
    – supports path-based endpoints
    – PKI method only
    • Azure
    – supports path-based endpoints (but only via exclusion paths)
    • Kestrel
    – does not support any isolation
    • Nginx
    – supports domain/sub-domain isolation
    – supports PKI and self-signed

    View Slide

  43. 43
    @leastprivilege
    Example: Sub-domains with Nginx
    server {
    listen 443 ssl;
    server_name identityserver.io;
    location / {
    proxy_pass http://localhost:5000;
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    }
    }
    server {
    listen 443 ssl;
    server_name mtls.identityserver.local;
    ssl_verify_client optional_no_ca;
    location /connect {
    proxy_pass http://localhost:5000;
    proxy_set_header X-SSL-CERT $ssl_client_escaped_cert;
    }
    }
    ASP.NET Core
    self-signed
    forward certificate on header

    View Slide

  44. 44
    @leastprivilege
    Reading Certificate from Header
    public void ConfigureServices(IServiceCollection services)
    {
    services.AddCertificateForwarding(options =>
    {
    options.CertificateHeader = "X-SSL-CERT";
    options.HeaderConverter = (headerValue) =>
    {
    X509Certificate2 clientCertificate = null;
    if(!string.IsNullOrWhiteSpace(headerValue))
    {
    byte[] bytes = Encoding.UTF8.GetBytes(Uri.UnescapeDataString(headerValue));
    clientCertificate = new X509Certificate2(bytes);
    }
    return clientCertificate;
    };
    });
    }
    public void Configure(IApplicationBuilder app)
    {
    app.UseCertificateForwarding();
    }

    View Slide

  45. 45
    @leastprivilege
    Certificate Authentication Handler
    public void ConfigureServices(IServiceCollection services)
    {
    services.AddAuthentication()
    .AddCertificate(options =>
    {
    options.AllowedCertificateTypes = CertificateTypes.All;
    options.RevocationMode = X509RevocationMode.NoCheck;
    });
    }

    View Slide

  46. 46
    @leastprivilege
    Enabling MTLS in IdentityServer
    var builder = services.AddIdentityServer(options =>
    {
    options.MutualTls.Enabled = true;
    // sets "mtls" sub-domain
    options.MutualTls.DomainName = "mtls";
    });
    builder.AddMutualTlsSecretValidators();

    View Slide

  47. 47
    @leastprivilege
    Verifying Access Token Ownership at Resource
    • Resource server needs similar setup
    – accept/validate client certificates
    – forwarding of certificates to application host
    • After normal access token validation
    – compare certificate thumbprint of TLS channel with cnf claim in access token
    – if they match, caller can prove that it has access to the same key material
    • proof-of-possession

    View Slide

  48. 48
    @leastprivilege
    Pipeline Overview
    public void ConfigureServices(IServiceCollection services)
    {
    services.AddAuthentication("jwt")
    .AddJwtBearer("jwt", options => { … })
    .AddCertificate(options => { … });
    services.AddCertificateForwarding(…)
    }
    public void Configure(IApplicationBuilder app)
    {
    app.UseForwardedHeaders(…)
    app.UseCertificateForwarding();
    app.UseAuthentication();
    app.UseConfirmationValidation(…)
    app.UseAuthorization();
    }

    View Slide

  49. 49
    @leastprivilege
    Summary
    • Asymmetric keys are easier to manage and secure than shared secrets
    • Private key JWT
    – allows securing both front- and back-channel with single key pair
    • TLS client certificates
    – allows binding access token to client
    – even for public clients

    View Slide