Last updatedSep 6, 2016
Improve this page

Query string hash

A query string hash (QSH) is an important technique to prevent URL tampering and secure HTTP requests when they are made via a browser. The QSH secures the HTTP method, the relative URI path, and the query string parameters. A QSH claim is a SHA-256 hash of a canonical request, sent within a JWT token as a custom claim. Atlassian products send QSH claims that must be validated by apps to prevent leaking sensitive data. Apps must send QSH claims within JWT tokens when making HTTP requests on Atlassian products. When an app makes a requests without a QSH claim, or with a bad QSH claim (one that doesn't match the request), then the Atlassian product will respond with HTTP status 401 Unauthorized. Likewise, apps that detect a bad QSH claim should respond with HTTP status 401 Unauthorized.

Canonical request

A canonical request is a normalized representation of the HTTP request. It consists of:

These elements are joined by ampersand.

Example

Canonical methodCanonical URICanonical query stringCanonical request
GET/param=fooGET&/&param=foo
POST/user POST&/user&

Canonical method

The canonical method is the HTTP method in upper case.

Example

HTTP methodCanonical method
getGET
GetGET
GETGET
POSTPOST

Canonical URI

The canonical URI is the relative URI path for the Atlassian product or app, that represents a specific resource for the request. As a relative URI path, the canonical URI can survive URL rewrites performed by a reverse proxy. Therefore the canonical URI discards the protocol, server, port, context path, and query parameters from the request URL. The canonical URI always starts with a / character. Therefore, empty-string is not permitted and the minimum canonical URI is just /. The canonical URI only ends with a / character if it is the only character. The canonical URI should not contain & characters. Therefore, any & characters should be URL-encoded to %26.

For requests targeting apps, Atlassian products will discard the baseUrl in the app descriptor. For requests targeting Atlassian products, apps discard the baseUrl sent in the lifecycle installed callback.

Example

Base URLRequest URLCanonical URI
https://addon.example.com/jira-connectorhttps://addon.example.com/jira-connector/
https://addon.example.com/jira-connectorhttps://addon.example.com/jira-connector/issue/issue
https://addon.example.com/jira-connectorhttps://addon.example.com/jira-connector/title&description/title%26description
https://example.atlassian.net/https://example.atlassian.net/rest/api/2/issue//rest/api/2/issue

Canonical query string

The canonical query string is a normalization of the query parameters, that accounts for the denormalization of query parameters. In other words, query strings may have parameters in any order, may have the same parameter keys multiple times, or stray characters that have no semantic meaning. Because of this, the same parameter meaning can be expressed in many different query strings. The canonical query string factors out the differences so the queries can be compared. The rules for normalization follow.

Ignore the JWT parameter

Example

Query stringCanonical query string
jwt=ABC.DEF.GHI
expand=names&jwt=ABC.DEF.GHIexpand=names

URL-encode parameter keys

Example

Query stringCanonical query string
enabledenabled
some+spaces+in+this+parametersome%20spaces%20in%20this%20parameter
connect*connect%2A
1+%2B+1+equals+31%20%2B%201%20equals%203
in+%7E3+daysin%20~3%20days

URL-encode parameter values

For each parameter concatenate its URL-encoded name and its URL-encoded value with the = character.

Example

Query stringCanonical query string
param=valueparam=value
param=some+spaces+in+this+parameterparam=some%20spaces%20in%20this%20parameter
query=connect*query=connect%2A
a=b&a=b
director=%E5%AE%AE%E5%B4%8E%20%E9%A7%BFdirector=%E5%AE%AE%E5%B4%8E%20%E9%A7%BF

URL-encoding is upper case

URL-encoded characters in query string hashes should be upper case, like %2A, not %2a.

Example

Query stringCanonical query string
director=%e5%ae%ae%e5%b4%8e%20%e9%a7%bfdirector=%E5%AE%AE%E5%B4%8E%20%E9%A7%BF

Sort query parameter keys

Example

Query stringCanonical query string
a=x&b=ya=x&b=y
a10=1&a1=2&b1=3&b10=4a1=2&a10=1&b1=3&b10=4
=A&a=a&b=b&B=BA=A&B=B&a=a&b=b

Example

Using a complex Jira request, a query like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
link=http%3A%2F%2Fion%3A2990%2Fjira%2Fsecure%2FIssueNavigator.jspa%3Freset%3Dtrue%26jqlQuery%3Dissuetype%2B%253D%2BBug&
startIssue=0&
totalIssues=2&
endIssue=2&
issues=issues%3DTEST-2%2CTEST-1&
tz=Australia%2FSydney&
loc=en-US&
user_id=admin&
user_key=admin&
xdm_e=http%3A%2F%2Fion.local%3A2990&
xdm_c=channel-acmodule-1564427223927602208&
cp=jira&
lic=none

Will be sorted like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
cp=jira&
endIssue=2&
issues=issues%3DTEST-2%2CTEST-1&
lic=none&
link=http%3A%2F%2Fion%3A2990%2Fjira%2Fsecure%2FIssueNavigator.jspa%3Freset%3Dtrue%26jqlQuery%3Dissuetype%2B%253D%2BBug&
loc=en-US&
startIssue=0&
totalIssues=2&
tz=Australia%2FSydney&
user_id=admin&
user_key=admin&
xdm_c=channel-acmodule-1564427223927602208&
xdm_e=http%3A%2F%2Fion.local%3A2990

Sort query parameter value lists

In the case of repeated parameters, concatenate sorted values with a , character.

Example

Query stringCanonical query string
ids=-1&ids=1&ids=10&ids=2&ids=20ids=-1,1,10,2,20
ids=.1&ids=.2&ids=%3A1&ids=%3A2ids=.1,.2,%3A1,%3A2
ids=10%2C2%2C20%2C1ids=10%2C2%2C20%2C1
tuples=1%2C2%2C3&tuples=6%2C5%2C4&tuples=7%2C9%2C8tuples=1%2C2%2C3,6%2C5%2C4,7%2C9%2C8
chars=%E5%AE%AE&chars=%E5%B4%8E&chars=%E9%A7%BFchars=%E5%AE%AE,%E5%B4%8E,%E9%A7%BF
c=&c=+&c=%2520&c=%2Bc=,%20,%2520,%2B
a=x1&a=x10&b=y1&b=y10a=x1,x10&b=y1,y10
a=another+one&a=one+string&b=and+yet+more&b=more+herea=another%20one,one%20string&b=and%20yet%20more,more%20here
a=1%2C2%2C3&a=4%2C5%2C6&b=a%2Cb%2Cc&b=d%2Ce%2Cfa=1%2C2%2C3,4%2C5%2C6&b=a%2Cb%2Cc,d%2Ce%2Cf

QSH claim

The QSH claim is a SHA-256 hash of the canonical request as UTF-8 bytes. The hash is hex encoded with lowercase a-f characters. The claim is sent inside the JWT token body so it is eventually signed with the shared secret.

Example

Canonical requestQSH claim
GET&/test¶m=valuebe16910858a41fd19ea5c1b4e9decca9a784d1024cb00b2158defe2f29dc86dd
POST&/rest/api/2/issue&43dd1779e33c34fae00c308d62e5dd153a32147d1bcb5d40b3936457fda0ece4