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

OAuth in 2021. What's new?

75681814fbbb90c9224ea5ed0f8324ee?s=47 Dominick Baier
January 27, 2021
170

OAuth in 2021. What's new?

75681814fbbb90c9224ea5ed0f8324ee?s=128

Dominick Baier

January 27, 2021
Tweet

Transcript

  1. OAuth in 2021 What’s new? Dominick Baier @leastprivilege

  2. 2 @leastprivilege Me • 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 • Co-Founder of Duende Software – the new home of IdentityServer – https://duendesoftware.com email dbaier@leastprivilege.com blog https://leastprivilege.com twitter @leastprivilege slides https://speakerdeck.com/leastprivilege
  3. 3 @leastprivilege IdentityServer https://github.com/DuendeSoftware/IdentityServer https://github.com/IdentityServer/IdentityServer4

  4. 4 @leastprivilege Agenda • OAuth 2.1 • Metadata, signing algorithms

    & key rotation • JWT profile for OAuth access tokens • Resource Isolation • JAR & PAR • Proof-of-possession access tokens https://duendesoftware.com/training/highsecurityoauth
  5. 5 @leastprivilege OAuth 2.1

  6. 6 @leastprivilege Current BCPs & Security Considerations 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/
  7. 7 @leastprivilege No more Implicit Flow • Implicit flow was

    a workaround in 2012 to overcome cross-origin AJAX limitations – solved now with CORS • Error prone – tokens are transmitted via URLs and can leak easily • log files • browser history • referrer headers • implementation errors • proxies – no solution for token injection attacks (without adding OpenID Connect)
  8. 8 @leastprivilege Implicit Flow Request GET /authorize ?client_id=app1 &redirect_uri=https://app.com/cb.html &response_type=token

    &state=j1y…a23 &scope=api1 api2
  9. 9 @leastprivilege Implicit Flow Response GET /callback.html #token=32x…133 &expires_in=3600 &token_type=Bearer

    &state=j1y…a23
  10. 10 @leastprivilege No more Password Grant • Exposes user's credentials

    to client – increased attack surface – credentials can leak in more places than just the authorization server – users are trained to type in credentials in multiple places • more prone to phishing attacks • Incompatible with modern authentication flows – FIDO – 2FA – federation
  11. 11 @leastprivilege Original Flows Application Type Flow Machine to Machine

    Client Credentials Flow Server-side Web Application OAuth Code Flow OpenID Connect Hybrid Flow Password Grant Native Desktop/Mobile Applications Implicit Flow OAuth Code Flow OpenID Connect Hybrid Flow Password Grant SPAs Implicit Flow Password Grant Code Flow
  12. 12 @leastprivilege Grand Unification Application Type Flow Machine to Machine

    Client Credentials Flow Interactive Application Code Flow + PKCE
  13. 13 @leastprivilege Authorization Code Injection GET /authorize ?client_id=app1 &redirect_uri=https://app.com/cb GET

    /cb?code=stolen client id/secret/stolen access token 3 1 2
  14. 14 @leastprivilege Mitigation: Proof Key for Code Exchange GET /authorize

    ?client_id=app1 &redirect_uri=https://app.com/cb &code_challenge=cy..fc GET /cb?code=xyz client id/secret code / code_verifier access token 3 1 2 code_verifier = random number code_challenge = hash(code_verifier)
  15. 15 @leastprivilege Redirect URI Validation Attacks • Problematic – https://*.somesite.com/cb

    • e.g. sub-domain takeover – https://somesite.com/* • find a page with an open redirector • find a page where attacker can inject content GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=9ad67f13 &redirect_uri=https://somesite.com/some_endoint
  16. 16 @leastprivilege OAuth 2.1 • RFC 6749 + RFC 6750

    + various BCPs == OAuth 2.1 – https://tools.ietf.org/wg/oauth/draft-ietf-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
  17. 17 @leastprivilege OAuth Metadata, Key Rotation & Signing Algorithms

  18. 18 @leastprivilege OAuth/OpenID Connect Metadata

  19. 19 @leastprivilege Key Rotation & Algorithms • Rotation – recommended

    to rotate keys regularly – must keep a history of old keys (depending on max access token lifetime) • Algorithms – OIDC mandates RS256 as a minimum – OpenBanking/PSD2 requires PS256 – FHIR requires EC-based algorithms
  20. 20 @leastprivilege JWT Profile for OAuth Access Tokens

  21. 21 @leastprivilege Access Tokens • RFC 6749 has no information

    about token format or content
  22. 22 @leastprivilege Self-contained Tokens • JWTs are convenient to use

    and have good library support { "typ": "JWT", "alg": "RS256", "kid": "mj399j…" } { "iss": "https://issuer", "exp": 1340819380, "nbf": 1340818761, "client_id": "client", "user_id": "bob", "scope": "scopeA scopeB", } Header Payload eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMD.4MTkzODAsDQogImh0dHA6Ly9leGFt Header Payload Signature
  23. 23 @leastprivilege JWT Claim Types (all optional) • iss (Issuer)

    – identifies the principal that issued the JWT • sub (Subject) – identifies the principal that is the subject of the JWT • aud (Audience) – identifies the recipients that the JWT is intended for • exp (Expiration Time) – identifies the expiration time on or after which the JWT MUST NOT be accepted for processing • nbf (Not Before) – identifies the time before which the JWT MUST NOT be accepted for processing • iat (Issued At) – identifies the time at which the JWT was issued • jti (JWT ID) – provides a unique identifier for the JWT https://tools.ietf.org/html/rfc7519#section-4.1
  24. 24 @leastprivilege Lessons learned with JWTs • In absence of

    a standard, every vendor came up with different claims and semantics – how to convey user and client ID – define additonal claims – nbf vs iat – scopes vs audiences – required vs optional – cross-JWT confusion and substitution attacks • Two specs aim to provide interoperable definitions – https://tools.ietf.org/html/rfc8725 – https://datatracker.ietf.org/doc/html/draft-ietf-oauth-access-token-jwt
  25. 25 @leastprivilege sub & client_id • client_id – OAuth client

    ID • sub – if user is present: sub as defined in OpenID Connect – if no user is present: OAuth client ID https://datatracker.ietf.org/doc/html/draft-ietf-oauth-access-token-jwt#section-2.2
  26. 26 @leastprivilege Other required claims • iss – identifies the

    principal that issued the JWT • exp – identifies the expiration time on or after which the JWT MUST NOT be accepted for processing • aud – identifies the recipient(s) that the JWT is intended for • iat – identifies the time at which the JWT was issued • jti – provides a unique identifier for the JWT
  27. 27 @leastprivilege Scopes in JWTs { "iss": "issuer", "aud": "audience",

    "exp": 1340819380, "iat": 1340818761, "jti": "random_id" "sub": "123", "client_id": "456", "scope": "scopeA scopeB" }
  28. 28 @leastprivilege Cross-JWT Confusion https://www.rfc-editor.org/rfc/rfc8725.html#name-cross-jwt-confusion

  29. 29 @leastprivilege Example JWT per Profile { "typ": "at+jwt", "alg":

    "RS256", "kid": "mj399j…" } { "iss": "issuer", "aud": "audience", "exp": 1340819380, "iat": 1340818761, "jti": "random_id" "sub": "123", "client_id": "456", "scope": "scopeA scopeB" }
  30. 30 @leastprivilege Resource Isolation

  31. 31 @leastprivilege Resource Isolation • OAuth uses the scope parameters

    to indicate "what" the clients wants to access The authorization and token endpoints allow the client to specify the scope of the access request using the "scope" request parameter. The value of the scope parameter is expressed as a list of space-delimited, case- sensitive strings. If the value contains multiple space-delimited strings, their order does not matter, and each string adds an additional access range to the requested scope. The authorization server MAY fully or partially ignore the scope requested by the client, based on the authorization server policy or the resource owner's instructions. RFC6749
  32. 32 @leastprivilege Example: GitHub Scopes https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/

  33. 33 @leastprivilege Example: Google Scopes https://developers.google.com/oauthplayground/

  34. 34 @leastprivilege Problems with Scope Model • Token can be

    re-used at multiple resources – makes tokens very powerful • in case of leakage – prohibits resource-specific processing • e.g. token content, encryption – concerns about token re-use • especially with absence of sender constraints • Can't prohibit that clients requests token that can be used at multiple resources
  35. 35 @leastprivilege Shared vs Resource-specific Access Tokens { "iss": "https://issuer",

    "iat": 1340818761, "exp": 1340819380, "client_id": "backoffice", "scope": [ "orders", "invoicing" ] "sub": "182jmm199", "more_data": "…" } orders invoicing trust boundary?
  36. 36 @leastprivilege Resource Indicators • RFC 8707 introduces new request

    parameter – resource • Indicates target service or resource to which access is being requested – must be an absolute URI – might be a physical network address – multiple resource parameters are allowed
  37. 37 @leastprivilege Authorize Request • Request access to one or

    more resource – can be combined with scope GET /as/authorize? client_id=client& response_type=code& state=xyz& redirect_uri=https://client/cb& resource=https://invoice.api.com& resource=https://customer.api.com 1
  38. 38 @leastprivilege Redeeming the Code • Resource for access token

    must be specified – returned token is for the requested resource only POST /as/token grant_type=authorization_code& redirect_uri=https//client/cb& code=abc& resource=https://invoice.api.com { "access_token":"ey…Yw", "token_type":"Bearer", "expires_in":3600, "refresh_token":"4L…C2" } 2 { "iss": "http://as.com", "aud": "https://invoice.api.com" "iat": 1588420700 "exp": 1588420800, "sub": "123", "client_id": "456" }
  39. 39 @leastprivilege Getting access to other Resources • Refresh token

    can be used to request additional access tokens POST /as/token grant_type=refresh_token& refresh_token=4L…C2& resource=https://customer.api.com { "access_token":"ey…Yw", "token_type":"Bearer", "expires_in":3600, "refresh_token":"4L…C2" } 3 { "iss": "http://as.com", "aud": "https://customer.api.com" "iat": 1588420700 "exp": 1588420800, "sub": "123", "client_id": "456" }
  40. 40 @leastprivilege JWT Secured Authorize Request (JAR) Pushed Authorize Request

    (PAR)
  41. 41 @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
  42. 42 @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
  43. 43 @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]
  44. 44 @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
  45. 45 @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 }
  46. 46 @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
  47. 47 @leastprivilege Proof-of-Possession (PoP) Access Tokens

  48. 48 @leastprivilege Weakness of Bearer Tokens • Bearer tokens are

    not bound to the client – attacker can replay leaked tokens Authorization: Bearer <token>
  49. 49 @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)
  50. 50 @leastprivilege PoP Today • Transport Layer – Mutual TLS

    – https://tools.ietf.org/html/rfc8705 • Application Layer – DPop – https://tools.ietf.org/html/draft-fett-oauth-dpop
  51. 51 @leastprivilege cnf Claim { "iss": "https://server.example.com", "client_id": "client1", "exp":

    1493726400, "cnf": { "some_key_material" } } HTTP/1.1 200 OK Content-Type: application/json { "active": true, "iss": "https://server.example.com", "client_id": "client1", "exp": 1493726400, "cnf": { "some_key_material" } } JWT Introspection https://tools.ietf.org/html/rfc7800
  52. 52 @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
  53. 53 @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))); } }
  54. 54 @leastprivilege Setting a Client Certificate static SocketsHttpHandler GetHandler(X509Certificate2 certificate)

    { var handler = new SocketsHttpHandler(); handler.SslOptions.ClientCertificates = new X509CertificateCollection { certificate }; return handler; }
  55. 55 @leastprivilege Calling the Token Endpoint static async Task<TokenResponse> RequestTokenAsync()

    { var client = new HttpClient(GetHandler()); var disco = await client.GetDiscoveryDocumentAsync("https://demo.duendesoftware.com"); var response = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = disco.MtlsEndpointAliases.TokenEndpoint, ClientId = "mtls", Scope = "api1" }); return response; }
  56. 56 @leastprivilege Proof-of-Possession with MTLS { "iss": "https://issuer", "exp": 1340819380,

    "nbf": 1340818761, "client_id": "182jmm199", "scope": "api1", "cnf": { "x5t#S256": "bwcK0esc3ACC3DB2Y5_lESsXE8o9ltc05O89jdN-dg2" } }
  57. 57 @leastprivilege Verifying Access Token Ownership at Resource 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(); }
  58. 58 @leastprivilege Application Level Proof-of-Possession • OAuth 2.0 Demonstration of

    Proof-of-Possession at the Application Layer (DPoP) An application-level sender-constraining for access and refresh tokens that can be used in cases where MTLS is not available. It uses proof-of- possession based on a public/private key pair and application-level signing. DPoP can be used with public clients and, in case of confidential clients, can be combined with any client authentication method. https://tools.ietf.org/html/draft-fett-oauth-dpop
  59. 59 @leastprivilege DPop Token Request POST /token grant_type=authorization_code code=xyz redirect_uri=https://…

    Dpop: eyJ0…3Lg
  60. 60 @leastprivilege DPop Proof Token (Token Request) { "typ": "dpop+jwt",

    "alg": "ES256", "jwk": { "kty": "EC", "x": "l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs", "y": "9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA", "crv": "P-256" } }. { "jti": "-BwC3ESc6acc2lTc", "htm": "POST", "htu": "https://server.example.com/token", "iat": 1562262616 }.[Signature] public key unique ID HTTP method HTTP URL signature proves knowledge of private key
  61. 61 @leastprivilege DPop Token Response • Access token contains the

    public key as confirmation method { "iss": "https://server.example.com", "client_id": "client1", "exp": 1493726400, "cnf": { "jkt":"0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I" } } Base64url encoding of the JWK SHA-256 Thumbprint of the public key (RFC 7638) https://tools.ietf.org/html/rfc7638
  62. 62 @leastprivilege Resource Access GET /resource Authorization: Dpop ey…MQ DPop:

    ey…bQ
  63. 63 @leastprivilege DPoP Proof Token (Resource Access) { "typ": "dpop+jwt",

    "alg": "ES256", "jwk": { "kty": "EC", "x": "l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs", "y": "9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA", "crv": "P-256" } }. { "jti": "e1j3V_bKic8-LAEB", "htm": "GET", "htu": "https://resource.example.org/resource", "iat": 1562262618 }.[Signature] same public key resource must create jkt and compare with access token signature proves knowledge of private key
  64. 64 @leastprivilege Summary • OAuth keeps evolving • OAuth 2.1

    – wraps up several guidance documents – removes protocol flows that turned out to be problematic • Additional specs strengthen OAuth security – JAR & PAR – Resource Indicators – PoP