As part of the upcoming 3.12 release of Jira and 2.7 release of Confluence, a neat little feature will be added that allows the two applications to communicate in a trusted way, such that it is possible for Confluence to request information from Jira on behalf of the currently logged-in user, but without having to re-authenticate the user on the Jira end. Sounds intriguing? Read on!
Confluence has a nifty macro called the Jira Issues Macro that allows users to embed issues into a page straight from Jira. While the macro works well, it is not entirely secure as you have to store Jira user credentials right there in the macro. The reasons we require the user credentials are clear. Firstly, your Jira instance might not be public, and enabling an anonymous account to access your issues is not an option. Secondly, you might have security restrictions on your issues, and so you don’t want to allow someone to leak issue data from your “Top Secret” project by using the Jira Issues Macro.
(click for more…)
In order to satisfy these requirements, but keep our credentials safe, we decided to “look up” to establishing trust on the application level, and in doing so, we get trust on the user level for free (or close to it). Our functional requirement now becomes: “If I (Jira) can trust that you (Confluence) are who you say you are, then I’ll authenticate the user you give me without their password”. The benefit of taking this approach is that trust only has to be established once between the two applications, and must be approved by a Jira system administrator. Once trust has been established, it is entirely transparent to the users of Confluence – they no longer have to provide their credentials to the macro, as we simply send the user name of the currently logged in user.
The problem of application trust is not a new one, and there are several established protocols which help solve these problems. When designing our solution, we examined a few of these protocols to see if we could simply implement them, or at least borrow ideas from them.
OpenID was one of the protocols examined. In a nutshell, OpenID is about sharing identities in a decentralised fashion, so that users can just refer an application to a single existing identity rather than having to manage an identity per application.
While OpenID is cool and helps us generally with the problem of identity management between Confluence and Jira, it unfortunately doesn’t give us any benefit when trying to conduct trusted communications between the two applications, as it is a identity/user-centric solution, not an application-centric one. It would also introduce additional dependencies on OpenID servers, which is something we can’t assume when devising a solution for the general user base.
OAuth is a younger protocol which was designed to grant consumer applications access to a user’s protected resources held by a service provider, without having to give away the user’s service provider credentials to the consumer application. This is achieved by redirecting the user between the applications and requiring them to authenticate themselves on both ends. Once authentication has been set up, the applications communicate with a shared token that encapsulates all the details of the request for resources.
At first glance, this protocol looks like a pretty good fit for our problem:
- consumer application == Confluence
- service provider == Jira
- protected resources == issues.
OAuth also does not require any additional infrastructure to work; the applications simply talk to each other, periodically requiring some user input. This however highlights one of the downsides of the protocol for our situation. It is very well suited to one-time requests for protected resources, where you want user supervision of the authentication for peace of mind. But in the context of Confluence and the Jira issues macro, you wouldn’t want the user to be redirected to Jira to authenticate themselves every time they viewed a Confluence page that contained the Jira issues macro.
One way to counter this is to again “look up”, and treat the entire consumer application as a single user, so that the one-time request for resources sets up the trust between Confluence and Jira indefinitely, and then all requests made by the Jira issues macro will just include the currently logged in user. This could work, but the effort required to make OAuth work didn’t seem worth it when we could roll our own solution. There was also a problem that, at the time when we were discussing implementation details, no Java libraries for OAuth were available to utilise (but this appears to have been remedied).
So after checking out what was already on offer and not finding anything suitable, we decided to roll our own protocol. In order to minimise engineering effort, it’s design is fairly straight forward:
Setting up trust between Jira and Confluence
- Jira sysadmin requests a trusted application authentication certificate from a Confluence instance by providing the base URL of the instance. The certificate contains Confluence’s Trusted Application ID and Public Key (generated specifically for use with the TAA protocol).
- Jira validates the certificate and asks the sysadmin for a few extra details about the trust relationship, such as Name, Timeout, Allowed IP Addresses and Allowed Request URLs.
- Jira stores all this information in the database.
Making a trusted request from Confluence to Jira
- Confluence sends a web request to Jira, appending additional headers to the request, including:
- Timestamp (nonce) of the request + user name of the currently logged in Confluence user, encrypted with a symmetric key (generated on the fly)
- The symmetric key, encrypted with Confluence’s private key
- Confluence’s application id (as stated when trusted was established)
- Jira attempts to decode the encrypted headers, using the stored information about the relationship. A couple of checks are conducted to validate the request:
- The trusted application id refers to a valid trusted application
- The user name specified exists in the Jira user base
- The agreed timeout length has not expired
- The request originated from a trusted IP address
- The resource being requested matches those specified in the URL Match list
- If any of these checks fail, a response is sent to Confluence, indicating the reason for failure. Otherwise, Jira will authenticate the specified user for the duration of the single request, and respond with the resources (i.e. the Jira issues)
As the protocol has a simple design, there are inherent limitations and risks to be considered. Firstly, this protocol can only work if the Confluence and Jira user bases are the same. Because Confluence automatically appends the currently logged in user to the request header, Jira will always return an error for that request if the same user does not exist in Jira. There is also a more subtle issue whereby the user does exist in Jira, but they do not have the correct level of permissions that you would expect (and hence do not get the correct set of issues returned by the macro). Of course, if you are using a single user base solution, such as Crowd, you wouldn’t have this problem.
Secondly, the protocol is not immune to certain security attacks such as man-in-the-middle and replay attacks, though it is considerably hard to pull these off successfully. For the man-in-the-middle scenario, the attacker needs to be present during the trust relationship establishment, so that their public key is registered with Jira instead of the intended client’s. But since the Jira sysadmin manually enters in the base URL of the client application, this means that the attacker needs to have poisoned the Jira instance’s DNS so that the domain specified (e.g. confluence.atlassian.com) points to the attacker’s domain (evilconfluence.attacker.com). If this is the case, then I’m afraid you have slightly more pressing concerns than unauthorised access to your Jira instance. To successfully pull off a replay attack, the attacker would have to be able to spoof the original client’s IP address, and they would also have to be able to forward their spoofed requests before the timeout window on requests closes. Security risks such as these will not likely be an issue for most Confluence and Jira customers however, as we expect the common scenario to be that both instances are running inside a trusted network.
“Trust no one unless you have eaten much salt with him” — Cicero
Finally, remember that the protocol is all about trusted relationships. I’m sure if Cicero was still around today (probably died of heart failure from eating too much salt), he would tell you that while this feature will allow you to do some cool things between Confluence and Jira, as a Jira admin, it’s your responsibility to ensure that you really can trust the Confluence instance you’re talking to.