Action required: Atlassian Connect vulnerability allows bypass of app qsh verification via context JWTs

The point is that, for a few customers, AP.context.getToken() is generating a JWT without qsh claim. We had to remove the qsh validation (for endpoints that verify jira context jwt only) in order to unblock those customers.

Is it expected that in some scenario AP.context.getToken() generates a JWT without qsh claim?

Yes it is if these customers are running on an old manifest.
The reason could be a recent permission scope change and these customers did not manually approve and update the app.
This will change once Atlassian makes the change for everyone, not just the ones with apiMigrations

3 Likes

Yes please @james.dellow, still set the apiMigrations. This ensures context JWTs can’t be used for iframe/module endpoints your app may have.

1 Like

Hi @umang.savaliya

we are not getting JWT with qsh on below confluence cloud descriptor

You should be, the JWT for renderModes isn’t sent via a query string in this case, it’s via an Authorization header. Please let me know if the JWT (with qsh) isn’t in Authorization header for /<appselector>/rest/getpdf

Regards,
Zac

Thanks for you answer.

So far i could see that we have 4 customer with a very old manifest. Ok, so as far as i understand, regardless apiMigrations in descriptor, Atlassian will force qsh in Jira context JWT. When exactly is this going to take place?

I guess that my other option is to ask the customers to manually approve the “new old” descriptor.

Hi
Starting today, and after doing the migration and passing the validation by the bot, my apps can’t be installed by new clients anymore failing with the following messages:

Authentication verification error (401): Invalid JWT: Algorithm from the header “RS256” does not match
Installation verification error: 401 clientKey in install payload did not match authenticated client

Was there something I miss? I don’t have a handler for Installed. Should I add one? Is anyone else seeing this?

Thanks
Jerry

I’m seeing the exact same error as @jerry.laster.

Question for the both of you @jerry.laster / @konrad.garus1: are you using ACE / ACSB?

If this is still going on and affecting production cloud customer - please create a new post in the cloud incident forum since that’s monitored by atlassian.

1 Like

ACE here.

It comes down to req.context.clientKey being the instance URL rather than UUID. Digging through the sources, it’s coming from unverifiedClaims.aud here. issuer is the UUID, unverifiedClaims.aud is instance URL.

  let clientKey = issuer;
  // The audience claim identifies the intended recipient, according to the JWT spec,
  // but we still allow the issuer to be used if 'aud' is missing.
  // Session JWTs make use of this (the issuer is the add-on in this case)
  if (!_.isEmpty(unverifiedClaims.aud)) {
    clientKey = unverifiedClaims.aud[0];
  }

Using ACE 7.1.3

Atlassian will be moving to the next phase of this fix, today.

We will begin insertion of the fixed qsh value into all ContextJWTs. If you have any concerns or you believe your app has been impacted, please comment here or raise a DEVHELP ticket for support.

3 Likes

Hello!
We’re using ACSB and having some new users cannot install the plugin from the marketplace since migration.
Thx.

This was fixed in ACE 7.1.4 to do with some signed install updates. It’s unrelated to what’s described in this thread.

I have opt-in to the updated context JWTs and the app is working fine when being installed via UPM in developer mode. However, app is not being installed through marketplace. Upon clicking install on marketplace, the installation endpoint for the app is not being hit.

Do we need to release another version for the changes to go live?

I am using the following dependencies on Spring boot system:

  • atlassian-connect-spring-boot-jwt 2.1.8
  • jira-rest-java-client-core 5.2.1
  • com.atlassian.fugue 2.7.0
  • jira-rest-java-client-app 5.2.0

Thank you!

Can you give me some more details

  • What is your app key?
  • What instance(s) does this occur on?
  • So, when you say the install endpoint is not being hit, what is happening?

@AndyJames the issues you’re seeing are unrelated to these changes. ACSB v2.1.9 has recently been published to remove the requirement that baseURL is unique, rather than apps using the client key as described in Ensuring your Atlassian Connect app handles customer site imports. This uniqueness check in ACSB was sometimes causing issues with installations and re-imports. Please let us know if you still encounter issues with ACSB v2.1.9.

1 Like

Hello @zsims
Thanks for the update. Any idea when it’s in the mavin repo? https://mvnrepository.com/artifact/com.atlassian.connect/atlassian-connect-spring-boot-starter

Is the qsh already supposed to be present in all ContextJWTs regardless apiMigrations configuration?

I’m asking it because i still can see a few customers (with old descriptors) generating ContextJWTs without qsh. Is my only option to ask them to update their descriptors?

Hey @HeyJoe,

I have a custom implementation of my connect back end app in Python. The app has been happily processing JWT tokenns with QSH claims for a couple years without incident. I patched my app for this fix by opting into context qsh claims.

My app only exposes webhooks and makes api calls. It does not have any front end components. Given this I was under the impression from the above threads that I would not need to make any change in the app.

I started seeing a small spike in qsh claims violations and that has continued to grow this morning, so I am now trying to figure out what I need to do with my custom implementations.

Assuming that somehow I also needed to accept a static qsh claim of the form ‘context-qsh’, I updated my qsh validation code to look like the following.

       claims = jwt.decode(token, verify=False)
        if claims['qsh'] != 'context-qsh' and claims['qsh'] != hash_url(http_method, url):
            raise DecodeError('qsh does not match')

I am still seeing the DecodeErrors being raised, and it looking at the logs it looks like I am getting a qsh that is not a static qsh or a match to the url hash.

By rights I should reject this, but it also seems to be coming from a legit install of the app, so I am not sure what I need to do here.

The line of code after this is

jwt.decode(
            token,
            audience=claims.get('aud'),
            key=self.get_shared_secret(claims['iss']),
            algorithms=self.algorithms,
            leeway=self.leeway)

and this seems to pass just fine, so its a token signed with the correct secret as far as I can tell.

Cant afford to throw away the valid calls from a client app, so for now I am thinking I will by pass qsh verification to keep the ship running.

Any advice is much appreciated. I am uncomfortable with accepting webhooks that fail qsh validation, but I don’t know what else to do.

Krishna

@OtavioMedeiros We are in the final transition phase so there will still be some tenants where the context JWTs may not contain the qsh claim. I will post an update here when this phase is complete.

1 Like