Philippe De Ryck

Hi, I'm Philippe, and I help developers protect companies through better web security. As the founder of Pragmatic Web Security, I travel the world to teach practitioners the ins and outs of building secure software.

Want to learn more about OAuth 2.0 and OpenID Connect?

Save yourself days of digging through dozens of specs with this online course

More information

Tamper-proof URL parameters with JWTs

URL parameters are straightforward to send information along in a request. Decades ago, we already used them to transport session identifiers, and today, many applications still use them for all kinds of purposes. But are URL parameters secure? What alternatives are there? Keep reading to find out more.

14 April 2020 API Security API, JWT, Crypto

The reality of URL parameters

In a world of application suites, OAuth 2.0, and OpenID Connect, URL parameters are present everywhere. In my consulting work and security assessments, I often encounter portal or gateway applications that give access to a suite of micro-applications. In such a scenario, the user can launch one of the applications from within the portal application. Doing so usually requires transferring a bit of context information from the portal to the client application. One example in a multi-tenant setup would be the identifier of a tenant.

When the portal launches a client, it either navigates the current context in the browser, or it opens a new browser tab. During that transition, there aren’t many ways to share data between the two applications. The most straightforward mechanism is to embed the necessary data directly in the URL of the client application. The query parameters of the URL are used to keep track of key/value pairs, containing everything needed to bootstrap the client application.

When looking at OAuth 2.0 and OpenID Connect flows, the same pattern occurs. When the flow transfers from the client application to the authorization server or identity provider, information is transported with URL parameters. When the flow returns, the same pattern is used.

Unfortunately, this pattern significantly affects the security of the data being transported. URL parameters are present in the URL sent by the browser, so they often leak to third parties. Additionally, attackers can easily manipulate the values in the URL. Finally, attackers often succeed in tricking the user into copying and pasting a URL containing sensitive parameters.

So how can you prevent this from happening?

Security through obscurity

URL parameters have been around for a long time. Their long history teaches us that they are easy to tamper with. Attackers can initiate requests with arbitrary values for the URL parameters.

As a developer, there is little you can do to prevent tampering with these parameters. A typical pattern that sidesteps the problem is the use of random or unpredictable identifiers. Instead of including the name or ID of the customer, the application embeds a randomly generated value, such as a GUID, in the URL. Doing so does not prevent tampering, but makes it virtually impossible to successfully guess other valid values for a parameter.

A URL with GUID identifiers, which are only meaningful to the application


https://manage.restograde.com
        ?tenant=d8cf3fa3-01a3-4c96-8502-a7051bfdc0a8
        &restaurant=5e4fd699-d6b8-4cd8-b1be-e5f0428c0918

Using random identifiers makes tampering with parameters harder, but does not address the root of the problem. The attacker can still tamper with the parameter; it just becomes more challenging to find the right value.

Tamper-proof parameters

The use of random identifiers corresponds to relying on obscurity for security. A better approach is guaranteeing the integrity of URL parameters. That way, any tampering by the attacker will be detectable to the application consuming the URL parameters.

Today, the easiest way to provide a set of key/value pairs is using a JSON Web Token (JWT). JWTs provide a way to exchange claims security between two parties. Under the hood, a JWT contains a JSON data structure, protected by a signature.

The URL in the example below uses a JWT to transfer information from one application to another.

A URL with the tenant and restaurant parameters embedded in a JWT.


https://manage.restograde.com
        ?params=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.
        eyJ0ZW5hbnQiOiJUaGUgQnVyZ2VyIEdyb3VwIiwicmVzd
        GF1cmFudCI6IkJ1cmdlciBNYXN0ZXIifQ.Sprs3Zx8GaF
        XbxJBenxKx6CEFPaUqsYq2xzR15ru1gKsOpH-0azGjiH-
        TZV-OdCyUFiEk-CKQbjb6_HbhuaySKn_YOuVu7jXcIGTk
        fkz4MWb6_TviND8TrKbPY3qjtOvi3bUvQ8hjPQEAJGhx-
        U7UZX6q98yZ6PJD34YgMkzkFYiobkcsTq9M7N6GM8kWVy
        iatP4tOsqOcn3rbFPbf_GBr8HpnGwh6jhPKAZEh8HTywA
        ZTfytgLoApQ6hUsGdDGKsKsUMo6mFqoVTLAdchU-Tw7Y7
        nDfmq9TsSc7lZroxnIqv-S0sZiVT-7tVUemTzmxoTL-ep
        3iFDoTrNJehQK0uA

The JWT looks like a bunch of nonsense but is a base64-encoded representation of the data. Decoding the JWT with jwt.io shows the actual contents of the JWT.

The JWT from the URL, with the payload decoded as a JSON object.


{
   "tenant": "The Burger Group",
   "restaurant": "Burger Master"
}

As you can see, the payload of the JWT now contains the parameters we wish to exchange. Apart from the data, the JWT also contains a header with metadata and a signature to guarantee the integrity of the data, which are not shown in the decoded version.

The pros and cons of using JWTs

The use of a JWT for transferring sensitive URL parameters has a couple of advantages, but also some disadvantages. Let’s take a closer look at what this means in practice.

A first advantage is the integrity protection of the data. The signature in the JWT guarantees that the receiver can detect unauthorized modifications to the data.

A second advantage is the extensibility of the JWT. If specific applications require additional parameters to be transferred, they can ] be added as new claims to the JWT. These new claims are automatically protected, which prevents sensitive parameters from becoming vulnerable to tampering.

A downside to the use of a JWT is the increased URL size. JWTs are a bit more verbose than simple URL parameters. However, the increase in size is limited and fixed. Since the payload of the JWT corresponds to the URL parameters, the only addition is the header and the signature. In this specific scenario, the size increase of using a JWT is approximately 374 bytes.

The technical details

Using JWTs for transferring URL parameters is not that hard. There are a few details to figure out, but once that is done, the mechanism is quite straightforward.

The most crucial detail is to determine how URL parameters will be used to share information. There are two main scenarios to consider. One is an application that uses parameters to track some information for itself. The other is an application transferring information to another application instance.

The first scenario uses a JWT within the boundaries of a single application. In such a scenario, the same application creates and consumes JWTs. That implies that the application can HMACs to protect the integrity of the JWT. HMACs are the most straightforward mechanism to guarantee the integrity of a JWT.

The second scenario is a bit more complicated, as it involves multiple applications. The use of an HMAC is not recommended, as it would require sharing the secret between multiple applications. Instead, the JWT should be signed by the application’s private key, allowing the receiver to verify the signature with the application’s public key. Implementing this mechanism requires a bit more effort, but also exhibits significantly better security properties.

I have written about the different signature schemes, along with the intricacies of key management for JWTs in another blog post. In this post, I also discuss several reserved JWT claims that can be used to provide relevant metadata about the JWT. A few highlights are:

  • Use the iss claim to identify the application that generated the JWT (e.g., https://sts.restograde.com)
  • Use the aud claim to identify the intended receiver of the JWT (e.g., https://manage.restograde.com)
  • Use the iat claim to keep track of the creation time of the JWT. Doing so allows the receiver to reject JWTs that are too old.

Finally, JWT libraries also offer support for encrypting the contents of a JWT. Using encrypted JWTs provides not only integrity protection but also confidentiality.

The disclaimer

Edited to clearly state the limitations of signed parameters (25 April 2020, based on feedback from Neil Madden )

Note that this article only discusses how to better protect static request parameters. Merely signing request data still leaves the data vulnerable to a replay attack. This concrete overview should demonstrate the benefits of using a JWT to transfer data in a URL:

  • Unsigned URL parameters are easy to tamper with. An attacker can easily start looking for valid values with a brute force enumeration attack.
  • Signed URL parameters, for example in a JWT, make brute forcing impossible. All parameters in the JWT are tied together and cannot be changed without being detected.
  • JWTs without replay protection can still be replayed. If an attacker observes request parameters in a JWT in request 1, nothing prevents them from using those parameters in request 2.

These security properties suffice for the use case discussed in this article. However, they do not suffice for more elaborate scenarios involving time-bound and transactional scenarios.

Summary

Unfortunately, URL parameters are essential to pass data around between different applications. One example use case is OAuth 2.0 and OpenID Connect. Another example is an application suite, accessible through a portal. An alternative to plain URL parameters is the use of a JWT. JWTs can be signed and encrypted, offering both integrity protection and confidentiality.

OAuth 2.0 is definitely moving forward in offering a JWT-based Signed Request Object as an alternative to URL parameters. You should consider the same for your applications.

Finally, note that ensuring the integrity of URL parameters has always been possible. However, before the mainstream adoption of JWTs, implementing such a mechanism required a lot of development effort. Today, thanks to the omniscient support for JWTs, that is no longer the case.



About Dr. Philippe De Ryck

Hi, I'm Philippe, and I help developers protect companies through better web security. Learn more about my security training program, advisory services, or check out my recorded conference talks.

Want to learn more about OAuth 2.0 and OpenID Connect?

Save yourself days of digging through dozens of specs with this online course

More information
Philippe De Ryck

Dr. Philippe De Ryck

Hi, I'm Philippe, and I help developers protect companies through better web security. As the founder of Pragmatic Web Security, I travel the world to teach practitioners the ins and outs of building secure software.


Talks and workshops

You will often find me speaking and teaching at public and private events around the world. My talks always encourage developers to step up and get security right.


Articles

Security is often about small nuances. In my articles, I dive deeper into various security topics, providing concrete guidelines and advice. My articles also answer questions I often get while speaking or teaching.


Security resources

Getting security right is all about knowledge. I strongly believe in sharing that knowledge to move forward as a community. Among my resources, you can find developer cheat sheets, recorded talks, and extensive slide decks.


Mailing list

Subscribe to the Pragmatic Web Security mailing list to stay up to date on the latest activities and resources.

Subscribe