How to apply Atlassian’s Security Requirements for addons

Around November 2019 Atlassian has published new security requirements for cloud applications. The requirements come into force on January 1, 2020. However Atlassian has not published guidance on how to apply these security requirements in their officially supported javascript framework ACE (Atlassian Connect Express).

In this post I share my interpretation of the security requirements in the form of javascript code for ACE. I’ll only cover the requirements which need to be handled in a typical ACE installation on e.g. Heroku. Heroku already provides TLS, and things like not exposing secrets in source repositories fall outside the scope of this article. The code uses two npm packages: helmet and express-sslify. You normally need to add the code below to your app.js.

// security configuration https://developer.atlassian.com/platform/marketplace/security-requirements/
import helmet from 'helmet';
import enforce from 'express-sslify';

After importing these, you need to configure the libraries:

...
// define helmet middleware early
// use helmet only in poduction/prodtest
let prodEnv = false;
if ( app.get('env') == 'production' || app.get('env') == 'prodtest') {
    prodEnv = true;
}
console.log('prodEnv is: '+prodEnv+' env is: '+app.get('env'));

// Sets "Strict-Transport-Security: max-age=34560000; includeSubDomains".
// HSTS minimum is one year, use 400 days
// fourhundredDaysInSeconds := 400 * 24 * 60 * 60 = 34560000
const fourhundredDaysInSeconds = 34560000;
if (prodEnv) {
    // for Heroku: trustProtoHeader: true, otherwise not necessarily
    app.use(enforce.HTTPS({ trustProtoHeader: true }));
    app.use(helmet.hsts({
        maxAge: fourhundredDaysInSeconds,
        includeSubDomains: false
    }));
    app.use(helmet.referrerPolicy({
        policy: ['no-referrer']
    }));
    app.disable('x-powered-by');
    console.log('started security policy');
}

...

// Mount the static files directory
const staticDir = path.join(__dirname, 'public');
app.use(express.static(staticDir));

// use the noCache after mounting the staticDir
app.use(helmet.noCache());

A short note on using noCache after mounting the staticDir: the static files usually are resource files like css and javascript and don’t contain changing data, so caching is allowed. I think this use cases falls under the exemption in article 5. of the security requirements. Using noCache after staticDir does not set the Cache-Control header for these resources.

The requirement that the application must authenticate and authorize all requests can be easily handled by ACE:

app.get('/configure', addon.authenticate(), function(req, res) {
     ...
    });

I.e. use addon.authenticate() in your route definitions. However make sure you don’t require authentication for atlassian-connect.json.

One further hint: Don’t use helmet.frameguard. ACE apps run as an iframe in the Atlassian application, and must be embeddable in an iframe.

Photo by REVOLT on Unsplash

How to upgrade ACE to AUI 8.5.1

Atlassian has developed AUI, the Atlassian User Interface. This is a set of UI components and a front end library do develop applications according to the Atlassian Design Guidelines. As of 2019-11, version 8.5.1 is the most recent version.

A second component to build apps on Atlassian is ACE, or Atlassian Connect Express, a framework in javascript. This framework currently uses an outdated ACE version by default. In the head of the html delivered by ACE, AUI 5.8.12 is used:

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="ap-local-base-url" content="{{localBaseUrl}}">
<title>{{title}}</title>
<link rel="stylesheet" href="//aui-cdn.atlassian.com/aui-adg/5.8.12/css/aui.css" media="all">
<link rel="stylesheet" href="//aui-cdn.atlassian.com/aui-adg/5.8.12/css/aui-experimental.css" media="all">
<!--[if IE 9]><link rel="stylesheet" href="//aui-cdn.atlassian.com/aui-adg/5.8.12/css/aui-ie9.css" media="all"><![endif]-->
<link rel="stylesheet" href="/css/addon.css" type="text/css" />
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="//aui-cdn.atlassian.com/aui-adg/5.8.12/js/aui-soy.js" type="text/javascript"></script>
<script src="//aui-cdn.atlassian.com/aui-adg/5.8.12/js/aui.js" type="text/javascript"></script>
<script src="//aui-cdn.atlassian.com/aui-adg/5.8.12/js/aui-datepicker.js"></script>
<script src="//aui-cdn.atlassian.com/aui-adg/5.8.12/js/aui-experimental.js"></script>
<script src="https://connect-cdn.atl-paas.net/all.js" type="text/javascript"></script>

The update to AUI 8.5.1 is not described in the documentation. Here is the header you need to use:

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="ap-local-base-url" content="{{localBaseUrl}}">
<title>{{title}}</title>
<script src="{{hostScriptUrl}}" type="text/javascript"></script>
  <link rel="stylesheet" type="text/css" href="https://unpkg.com/@atlassian/aui@8.5.1/dist/aui/aui-prototyping.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
<script src="https://unpkg.com/@atlassian/aui@8.5.1/dist/aui/aui-css-deprecations.js"></script>
<script src="https://unpkg.com/@atlassian/aui@8.5.1/dist/aui/aui-prototyping.js"></script>
<link rel="stylesheet" href="/css/addon.css" type="text/css" />

It’s simple once you know what to do.