How it breaks series: Authorisation rewriting (Part 1)

by SiJing Zheng on 28 August 2026

Our “how it breaks” series of blogs is specially written for developers. Each blog will demonstrate how commonly used functionality can break from a security point of view. The goal is to help you avoid these mistakes in your code and configuration.

Rewriting into a JSON body

If you are a developer who works with JSON, it is worth taking some time to understand an issue called “JSON parser differentials” (see this article by Bishop Fox).

It is easy to assume that a JavaScript API gateway which deserialises and reserialises a JSON request body is safe because it collapses all duplicate keys. However, issues can still arise if the downstream service uses a framework with case insensitive JSON request model binding.

An API gateway that verifies the user’s JWT and injects the subject into the request’s JSON object may do the following:

jwt = require('jsonwebtoken');
payload = jwt.verify(token, JWT_SECRET);
request.body["userid"] = payload.sub; // Or request.body.userid = userid;

This gateway validates the JWT, then sets the userid in the request body to the subject. This way an attacker editing their profile to add their own userid value would have it rewritten by the API gateway:

API gateway overwrites attacker provided value

All but the last value of duplicate JSON keys are removed when the request body is converted to a JavaScript object. It is then overwritten as shown above.

API gateway discards the first value, overwriting the last attacker provided value

However, JSON keys and JavaScript object properties are case sensitive. As a result, it is possible to include duplicate keys of different cases which are sent downstream. When used in ASP.NET Core for binding the request body to a model, Newtonsoft.Json parses them using case insensitively. This in combination with the last key precedence results in the API gateway value:

API gateway inserts userid as the last key, causing Newtonsoft.Json to overwrite the attacker provided value

Taking advantage of JavaScript’s insertion ordering and updates that do not change the insertion order, adding a userid key before Userid allows Userid to remain last and not be overwritten:

API gateway updates existing userid value, keeping it above the injected value

Developers can be mislead when considering System.Text.Json and JsonPropertyName("userid"), thinking that this will resolve this issue due to its default case sensitivity. However, in an attempt to maintain backwards compatibility with Newtonsoft.Json, these defaults do not apply to ASP.NET Core.

During deserialisation, Newtonsoft.Json performs case-insensitive property name matching by default. The System.Text.Json default is case-sensitive, which provides better performance as it is using exact matches. For information about how to do case-insensitive matching, see the Case-insensitive property matching in the image below. If you are using System.Text.Json indirectly by using ASP.NET Core, you do not need to do anything to obtain behaviour like Newtonsoft.Json. As ASP.NET Core specifies the settings for camel-casing property names and case-insensitive matching when it uses System.Text.Json.

Microsoft helpfully ensures backwards compatibility

Conclusion

Rewriting a request’s headers or body parameters without validation can lead to authorisation bypasses if the downstream service parses data differently. As a result strong validation and a fail fast design are recommended to mitigate these risks.

As a developer, the following steps can help you write and review safer code:

Ready to improve
your security posture?

Talk to us about penetration testing, security training, or a tailored advisory engagement.

Make an enquiry