mattmorg55 / Owin.Security.Keycloak

Keycloak Authentication Middleware for the C# OWIN Pipeline
http://keycloak.jboss.org
MIT License
17 stars 20 forks source link

JWT Signature is not validated properly #14

Open Smallwood19 opened 5 years ago

Smallwood19 commented 5 years ago

It is possible to manipulate the JWT being sent to this library. The existing calls to the Microsoft ValidateSignature method (jwt = ValidateSignature(securityToken, validationParameters);) fail to compare the token against the signature.

Assuming Chrome is your browser and you have some sort of web front-end, you can follow these steps to reproduce the security hole:

  1. Login into the application using a login with reduced privileges (assuming you have a basic role)
  2. After successful login, open developer tools of the browser (click F12 ) and go to Application tab.
  3. Look into ‘Session Storage’ or ‘Local Storage’ or ‘Cookies’, The ‘access_token’ would be preserved in one of those local storages.
  4. Take the ‘access token’ and go to https://www.jsonwebtoken.io/, try to decode the JWT token.
  5. You can see JWT the content in plain English. Then you can manipulate your role(s) into an ADMIN role (assuming you know the higher roles already). In general, you will be editing one of the JSON arrays/objects in the decoded JWT: "resource_access": { "my-app": { "roles": [ "MY_USER_ROLE" ] },
  6. ‘JWT string’ section on the website will update the encoded string after your manipulation, take that and update the ‘access_token’ in the local storage where you found the original one.
  7. Back to browser and refresh the Application.
  8. If the app is not strongly validating the signature of the token, it would open up all the features of ADMIN role. If security is good, API/UI should show some error message and does not display the ADMIN features.

These steps only work if your front-end also fails to validate the token signature before sending to the API.

Smallwood19 commented 5 years ago

I made a fork and added some code from BouncyCastle to perform the signature validation :(https://github.com/Smallwood19/Owin.Security.Keycloak)

highbyte commented 5 years ago

I'm not able to reproduce this behavior. The Microsoft method ValidateSignature seem to work correctly for me when using the Keycloak .NET library in a WebApi application.

When testing this I didn't have a Web client that stores the access token in local storage (so no "access_token" in cookie etc). But instead I manipulated the JWT access token manually before sending it in the Authentication HTTP header (Bearer: xxxx) to the WebApi application, changing the realm_access/roles. The Keycloak library fails as expected in the ValidateSignature method when doing this, as the JWT payload no longer matches the JWT signature.

The access tokens generated by my Keycloak server uses RS256 encryption (which uses public/private key pair, where as HS256 would use a shared encryption secret). The JWT header for me looks something like this: { "alg": "RS256", "typ": "JWT", "kid": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" }

Which settings are your settings when initializing the Keycloak .NET library? app.UseKeycloakAuthentication(new KeycloakAuthenticationOptions { ... }

Smallwood19 commented 5 years ago

I am using the following packages and settings:

<package id="Microsoft.IdentityModel.Logging" version="1.1.4" targetFramework="net461" /> <package id="Microsoft.IdentityModel.Protocol.Extensions" version="1.0.4.403061554" targetFramework="net461" /> <package id="Microsoft.IdentityModel.Tokens" version="5.1.4" targetFramework="net461" />

<package id="System.IdentityModel.Tokens.Jwt" version="5.1.4" targetFramework="net461" />

Settings:

            SignInAsAuthenticationType = persistentAuthType,
            AuthenticationType = persistentAuthType,
            DisableAllRefreshTokenValidation = true,
            ForceBearerTokenAuth = true,
            EnableBearerTokenAuth = true,
            TokenClockSkew = TimeSpan.FromSeconds(2)

My Keycloak Server Version is: 4.8.2.Final

My encryption is also RS256.

I too was expecting the Microsoft ValidateSignature method to throw an error, but for some reason with this combination it let it through. My colleague and I both watched it in disbelief. I'm not sure what else could have let that happen. Your code works great.

Thanks.

On Thu, Apr 4, 2019 at 4:56 AM highbyte notifications@github.com wrote:

I'm not able to reproduce this behavior. The Microsoft method ValidateSignature seem to work correctly for me when using the Keycloak .NET library in a WebApi application.

When testing this I didn't have a Web client that stores the access token in local storage (so no "access_token" in cookie etc). But instead I manipulated the JWT access token manually before sending it in the Authentication HTTP header (Bearer: xxxx) to the WebApi application, changing the realm_access/roles. The Keycloak library fails as expected in the ValidateSignature method when doing this, as the JWT payload no longer matches the JWT signature.

The access tokens generated by my Keycloak server uses RS256 encryption (which uses public/private key pair, where as HS256 would use a shared encryption secret). The JWT header for me looks something like this: { "alg": "RS256", "typ": "JWT", "kid": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" }

Which settings are your settings when initializing the Keycloak .NET library? app.UseKeycloakAuthentication(new KeycloakAuthenticationOptions { ... }

— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/mattmorg55/Owin.Security.Keycloak/issues/14#issuecomment-479832357, or mute the thread https://github.com/notifications/unsubscribe-auth/AqvFBpdF8I9I-_fXDa1gQA-SeTTKeZMXks5vdcxngaJpZM4caobg .

-- John Lewis Consulting 618-444-5131 johnlewisconsulting@gmail.com

highbyte commented 5 years ago

I'm using the same Microsoft .NET library versions, and Keycloak server 4.8.3.

The only way I've been able to bypass signature validation is to set option KeycloakAuthenticationOptions.AllowUnsignedTokens=true (which you shouldn't do!), and skipping sending the JWT signature part of the Access Token to the API.

By looking at the Microsoft method JwtSecurityTokenHandler.ValidateSignature(string token, TokenValidationParameters validationParameters) https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/rel/5.1.5/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs it seem to do the correct thing. The additional call you invoke in your fork (to JwtSecurityTokenHandler.ValidateToken) will eventually call the same method (ValidateSignature). So it's a bit of a mystery to me.

Thanks, but I cannot take credit for the library as a whole, I've only made a few bug fixes and compatibility with newer Keycloak server versions.

Smallwood19 commented 5 years ago

I appreciate you taking a look at this. If we ever discover what allowed us to circumvent the exception handling, we'll let you know.

On Mon, Apr 8, 2019 at 4:28 AM highbyte notifications@github.com wrote:

I'm using the same Microsoft .NET library versions, and Keycloak server 4.8.3.

The only way I've been able to bypass signature validation is to set option KeycloakAuthenticationOptions.AllowUnsignedTokens=true (which you shouldn't do!), and skipping sending the JWT signature part of the Access Token to the API.

By looking at the Microsoft method JwtSecurityTokenHandler.ValidateSignature(string token, TokenValidationParameters validationParameters)

https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/rel/5.1.5/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs it seem to do the correct thing. The additional call you invoke in your fork (to JwtSecurityTokenHandler.ValidateToken) will eventually call the same method (ValidateSignature). So it's a bit of a mystery to me.

Thanks, but I cannot take credit for the library as a whole, I've only made a few bug fixes and compatibility with newer Keycloak server versions.

— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/mattmorg55/Owin.Security.Keycloak/issues/14#issuecomment-480756215, or mute the thread https://github.com/notifications/unsubscribe-auth/AqvFBmfL9W4ssrB-uNdKbWg7kpRRgX2Rks5vewu0gaJpZM4caobg .

-- John Lewis Consulting 618-444-5131 johnlewisconsulting@gmail.com