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.
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:
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.
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:
userid as the last key, causing Newtonsoft.Json to overwrite the attacker provided valueTaking 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:
userid value, keeping it above the injected valueDevelopers 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.Jsonperforms case-insensitive property name matching by default. TheSystem.Text.Jsondefault is case-sensitive, which provides better performance as it is using exact matches. For information about how to do case-insensitive matching, see theCase-insensitiveproperty matching in the image below. If you are usingSystem.Text.Jsonindirectly by using ASP.NET Core, you do not need to do anything to obtain behaviour likeNewtonsoft.Json. As ASP.NET Core specifies the settings for camel-casing property names and case-insensitive matching when it usesSystem.Text.Json.

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:
Talk to us about penetration testing, security training, or a tailored advisory engagement.
Make an enquiry