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.