What CSPs to set on Atlassian Connect apps

This is not a question. It’s a summary of the current understanding of the community about the HTTP headers for the Content Security Policy (As Murphy’s law says, the best way to have the correct answer is to post the wrong one on the Internet).

CSPs cannot be set to *, because it’s a security malpractice, i.e. hackers could impersonate your app and display it in an iframe. So the CSPs are often something along the lines of self + https://$SUBDOMAIN.atlassian.net (replace $SUBDOMAIN with your customer’s subdomain).

CSPs cannot be set to *.atlassian.net because of custom domains. Jira and Confluence can be displayed on the customer’s custom domain, not .atlassian.net.

Where to find the customer’s custom domain? There are 2 ways: 1. It is part of the /installation payload (see displayUrl vs baseUrl in the docs) and this is the most reliable one. However, it requires to make your CSPs dynamic. 2. The custom domain URL is also provided in the xdm_e parameter. However the xmd_e was marked as deprecated in 2019, then reinstated in 2021. This lacks official documentation on the topic.

{ # /installation payload
  "key": "installed-addon-key",
  "clientKey": "unique-client-identifier",
  "sharedSecret": "a-secret-key-not-to-be-lost",
  "baseUrl": "https://example.atlassian.net",
  "displayUrl": "https://issues.example.com",
  "displayUrlServicedeskHelpCenter": "https://support.example.com",
  "productType": "jira",
  "description": "Atlassian Jira at https://example.atlassian.net",
  ...
}

Beware of xdm_e: xdm_e cannot be used if the JWT token isn’t validated. Therefore it is impossible to display an authentication error to the user within an iframe of a custom domain’s Jira instance, because the JWT is not validated, therefore the xdm_e is not validated and subject to hijacking, therefore you cannot set the CSP, therefore CORS will reject the display of your error page within the iframe. You could be tempted to use the clientKey to find the url from the database, but you’d also be leaking info to a non-authenticated request.

The result
Please correct me if these CSPs are incorrect.

default-src 'none'; # We block everything
font-src 'self'; # self = accept all sources coming from the origin URL
connect-src 'self';
script-src 'self' https://connect-cdn.atl-paas.net; # This is for all.js
style-src 'self' 'unsafe-inline' https://connect-cdn.atl-paas.net;
# Here are the dynamic ones, replace $CUSTOM_DOMAIN and $SUBDOMAIN with the value
img-src 'self' $CUSTOM_DOMAIN https://*.atlassian.net https://api.media.atlassian.com https://*.atl-paas.net ;
frame-ancestors $CUSTOM_DOMAIN https://$SUBDOMAIN.atlassian.net;

Is everything correct?
Thanks to @scott.dudley and @AdamAhmed for their input on this.

2 Likes

For my own understanding, is this advice for a developer wanting to set CSP headers on the iframe response of their Connect app? Given the category this was posted in, is adoption of Forge involved somehow (setting them in Connect to match Forge to confirm that it’ll work once the UI is moved over to Forge)?

Thanks for writing up the discussion. I would suggest not using *.atlassian.net since anyone can create one of those. If you are already grabbing displayUrl you can also grab hostBaseUrl (or it looks like it’s called baseUrl in the install payload?) so that you are locked down to specifically this Atlassian site.

There is the (now closed) RFC-35: Custom Domains Support For Confluence - #42 by JohnHooper , however in this discussion Atlassian says:

So I’m not aware of Atlassian guidance how vendors should handle the CSP for custom domains.

Hi James, it looks like the “Connect” category was renamed into “Adopting Forge from Connect”, which is clever marketing.

This topic was automatically closed after 30 days. New replies are no longer allowed.