Tamperproof HTTP Requests

Table of contents

Tamperproof HTTP Requests
In this article we explore data integrity and authenticity when dealing with sensitive information.
This section outlines the fundamental importance of making HTTP request bodies tamperproof, particularly when handling personally identifiable information (PII). We introduce digital signatures as a key mechanism for data integrity.
Why should you care?
When extending the user registration process, using Auth0 (an OIDC Provider), it is important to ensure data integrity and authenticity. Particularly when dealing with sensitive information that could lead to personal identity identification (PII) like the national register numbers.
In this blog post, I will explore difficult ways how to achieve this. Making the HTTP
request body is tamper-proof is a step in protecting data as it moves from one server to another. The use of signatures for HTTP
request bodies provides a robust mechanism for ensuring data integrity and security in this context. Frameworks like JOSE
(JSON Object Signing and Encryption) can help in this matter.
A lot of the internet is already protected by using the HTTPS
protocol, right? Well, HTTPS
only encrypts the data; it does not guarantee that the content is not tampered with during transit. HTTPS
provides a secure channel but does not protect against all types of tampering. For instance, if data is decrypted and re-encoded, the integrity may be compromised without detection. In scenarios where sensitive data is transferred – such as through webhooks – signing the request body ensures the data's integrity independently of the transport layer's security.
HTTP
request body signatures add a layer of security by allowing the recipient to verify that the body has not been altered in transit. They act as a digital signature, much like those used in emails, ensuring the integrity of the sent content.
I will also investigate if using an access or ID token could help in this matter.
Previously on ...
Here, we recap earlier discussions on signatures and OpenID Connect, setting the stage for a deeper exploration into data security practices in HTTP requests.
I wrote about OpenID Connect before and how to mock your provider, and thus creating and faking tokens...
- https://dotnet.kriebbels.me/oauth2-open-id-connect-accessidtoken
- https://dotnet.kriebbels.me/signature-validation-required-microsoft-says-no
- https://dotnet.kriebbels.me/how-to-read-a-claim-from-a-token-in-dotnet-6
- https://xebia.com/blog/openid-connect-mocking-you/
- https://xebia.com/blog/openid-connect-mocking-you/
I did a talk as well on how to mock your OIDC Provider:
- https://www.visug.be/Events/92
- https://www.updateconference.net/en/2023/session/mock-your-openid-connect-provider
This article aims to further expand upon previously covered concepts and enhance understanding.
Understanding JWT
, JOSE
and Access/ID/... Tokens
Before we explore individual token types, this section provides an overview of JWT
and its role within the broader JOSE
ecosystem. That is important for safeguarding data communicated via OpenID Connect and OAuth2 protocols.
OpenID Connect and OAuth2 protocols often leverage JWT
to communicate authorization and identification details.
Discussions with colleagues and customers highlight common misuses of JWT
as information repositories, necessitating a balance between security and data sensitivity.
There are a lot of tokens in the OpenID Connect world, but because the goal of this document is about making something tamperproof, we will focus on the access token as an example.
Access Tokens Explained
In this section, gain insights into access tokens, particularly within the JWT
format, and how they serve as credentials for accessing protected resources, highlighting their role in ensuring authenticity and confidentiality.
An access token is a credential used to access protected resources. When used in a JWT
format, it typically contains claims about the user and any permissions granted. These tokens can be JSON Web Signature (JWS
) or JSON
Web Encryption (JWE
) encoded to ensure authenticity and confidentiality, respectively.
An ID token contains information about the user when access is granted. This is always in a JWT
format.
These tokens can (should!) have a JSON
Web Signature (JWS
). This ensures authenticity.
When the information is really sensitive, they can be encrypted using the JSON
Web Encryption (JWE
) to ensure confidentiality.
Making the tokens tamperproof
Learn how the JOSE
framework enables tamper proofing of access tokens. This section describes the use of JSON Web Signatures and JSON
Web Encryption for securing token integrity and confidentiality.
By making the access token tamperproof, the JOSE
framework is used to Secure the Payload.
The JSON Object Signing and Encryption (JOSE) framework offers a suite of technologies to secure communications:
JWT
: JSON-based claims as a token.JWS
: JSON Web Signatures to secure and protect against tampering.JWE
: JSON Web Encryption to protect sensitive data.JWK
: JSON Web Key defines cryptographic keys in JSON.
JOSE
enables you to sign and encrypt HTTP request bodies, ensuring integrity and confidentiality.
Understanding the Versatility of JSON Web Tokens
JSON
Web Tokens (JWT
) are often thought of as mere access tokens, but their potential extends far beyond this basic use case. A JWT is a compact, URL-safe means of representing claims between two parties. These tokens are particularly useful in scenarios that require verification and integrity. In this documentation, we will explore how
JWT`s can be utilized beyond traditional access tokens and how they can ensure the integrity and non-tamperability of HTTP request bodies.
The Concept of 'Super Tokens'!
Overloading access tokens or ID tokens with too much information can lead to what is known as "super tokens". These are risky because they could get logged in various places (e.g., proxies, logs), potentially exposing sensitive data unnecessarily. Instead, balancing token content with just enough claim data ensures better security and usability.
Including too much in a single token can lead to technical problems as well. When a token gets to big, it can not be saved into a cookie, or used in the URL of a browser.
Storing large amounts of data in a token can lead to performance issues. Tokens should remain lightweight to travel over networks efficiently.
Super tokens present a single point of failure. If a super token is compromised, it can grant extensive access, leading to potentially severe security breaches. Managing permissions using super tokens ensure that only the right users have access to certain parts of a system can become complex and error-prone.
What should not be stored in a token?
Highly sensitive data such as passwords, credit card details, or private keys should never be stored in tokens. This is because tokens are often stored in less secure environments, such as cookies or local storage, which could lead to exposure if an attacker gains access.
Information that is subject to frequent change should not be stored in tokens, as updating a token across a system can be cumbersome. A token is valid for the specified time. This means that, in that time window, that data is valid.
HTTP
Header vs HTTP
Body?
Tokens are commonly attached to HTTP
headers for authentication purposes. For instance, an access token in the header can authenticate API requests.
Headers should only contain information necessary for message transmission like authentication tokens and content type identifiers. Headers should be lightweight and used to convey metadata.
The body is used for the main content of the HTTP
message. Sensitive data that is sensitive, dynamic or is not used for authorization should be safely passed here...
However, it becomes essential to ensure the tamper-proofing of the request body.
And thus, we have the reason why I did some digging into this topic: Making the body tamperproof can be done by
- Levering the
JOSE
framework - Using a Signature in the
HTTP
Header
JWT
Structure
Above, we discussed that the JOSE
framework has two formats:
JWT
JWE
A JWT
consists of three parts:
- Header: Contains the type of token (
JWT
) and the hashing algorithm used. - Payload: A set of claims, which can be any information you want to share.
- Signature: Used to verify the integrity and authenticity of the token.
Example of a JWT
signed with a shared secret (HS256):
// Header
{
"alg": "HS256",
"typ": "JWT"
}
// Payload
{
"sub": "1234567890",
"name": "John Doe",
"scope": "openid profile email",
"country": "belgium",
"email": "john@doe.com"
}
// Signature
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
Body and JWT
in HTTP
Requests
In this section, read how JWT
can enhance HTTP request body security, ensuring data sent is from an authentic source. We cover encoding sensitive data within JWTs for tamper proofing.
While JWT
are commonly used for authentication, they are not limited to being access tokens. They can also serve as secure message carriers or embedded within HTTP request bodies to ensure data integrity.
The following steps outline the process:
By applying
JWT
encoding on the body of an HTTP request, any alteration to the body would be detected through signature verification. This ensures that the data is indeed from an authentic source and has not been tampered with on its way to the server.You can encode sensitive parts of the HTTP message (for example, headers or the body itself) into the
JWT
payload. Only an entity with the correct secret or public key can validate theJWT
and trust the data.Using
JWT
not in an authorization context simplifies the process. There's no need for a centralized authority to verify token signatures. Just an agreement between two parties and where to find a shared secret or the location of the public key.
Example HTTP Request
An example use case could be a secure data submission where JSON payload (representing a transaction) is signed using JWT
technology before it is sent to prevent unauthorized changes. The National Register Number (nrn
) is the real identification of the user.
POST /transaction HTTP/1.1
Date: Fri, 11 Oct 2024 09:38:52 GMT
Host: api.dummy.com
Content-Type: application/json
{
"amount": "1500",
"currency": "USD",
"recipient": "Alice",
"jwt": "eyJhbGciOiJSUzI1NiJ9.ewogICJucm4iOiI4MzEyMDcwMjQ1MCIKfQ.ezfyvYmmQ4Cq8nXS1Hy0Duo9i-4yooV3BHk3NLbF2dKMnBgpymsigq951AQ1d_sXEfVOGlOqXSGynV8PL2vR52lO1vOXHU8Cm4yoCgCAwyG5k9XWCi9DSiBj8SXT0xLxXI94p0Oy7COhNUeEnoVrRRJ4XeTLB0bBPuFVjFgJT4435ZnkRMsY7oHe7czSdg1lL-ZgFjirq2YVAbx5wI1DyJ0QPiIVN126pZ-Tsx06I9pCukQARPDqLMESszmd7rx1e4OcaW4GnFicGSkQ8pqOYIXzfSpT-NNlHSlygSioCrF9VgwPbPZDyzqJNcrAQovMrLCyIliD92GPeVHinQdrog"
}
After decoding the field jwt
, it should resemble the following request
POST /transaction HTTP/1.1
Date: Fri, 11 Oct 2024 09:38:52 GMT
Host: api.dummy.com
Content-Type: application/json
{
"amount": "1500",
"currency": "USD",
"recipient": "Alice",
"jwt": {
"nrn": "83120702450"
}
}
Or you can sign the entire body with the needed change on the Content-Type
header
POST /transaction HTTP/1.1
Date: Fri, 11 Oct 2024 09:38:52 GMT
Host: api.dummy.com
Content-Type: application/jwt
eyJhbGciOiJSUzI1NiJ9.ewogICJhbW91bnQiOiAiMTUwMCIsCiAgImN1cnJlbmN5IjogIlVTRCIsCiAgInJlY2lwaWVudCI6ICJBbGljZSIsCiAgImp3dCI6IHsKICAgICAgICAibnJuIjogIjgzMTIwNzAyNDUwIgogICAgfQp9.QCC1KQn9lTAaPXqokVySRdMlLRoC4ALiQjagyyZbSPkicFe5HyEUZoEB9PtXLMM8LCc9-vt4ygiRhitImT_WLHNmBSBkubrbUwO8FxZ_GSZFuPrA5vnk4F63Ro6N86krvE46cHOX3-q-2AByl82leRlv_LywTfQIw-TnOvwfNaL5bCGDa9rv7bbZd2LD7FusV0t7_oU0ZCPpOHq-FolKQCLOy9RUzXzkxWDlYNw5AlTfkQ_RYV9QjwmfNYMwh2BnmpxfBSDc0aqSfPmcvD2c_J27hznGjkCORLYjNoprhJw2_F8JDuVF38JxD-8Kvr-G0_x5E0oLRF5aBLdc1vp-tQ
JSON Web Encryption (JWE)
This section explains the structure and utility of JWE tokens, detailing their multiple encryption layers to provide comprehensive data security.
JWE tokens encapsulate multiple encryption layers. Here's a breakdown of its structure:
JWE
Encrypted Key: This is the encrypted version of the Content Encryption Key (CEK
). TheCEK
is essential for decrypting the actual payload.JWE
Initialization Vector: This element maximizes security by ensuring that identical data never results in the same encryption output.JWE
Authentication Tag: This ensures that the data integrity is maintained, verifying that the data has not been tampered with.JWE
Ciphertext: This is the encrypted data payload that you aim to secure.
Example: Deciphering the JWE
Token
To decrypt a JWE
token, follow these steps:
- Understand the
JWE
Header:- Identify the encryption algorithm used from the JWE header.
- Request Decryption for
JWE
Encrypted Key:- Send a request to the server to obtain the Content Encryption Key (
CEK
).
- Send a request to the server to obtain the Content Encryption Key (
- Decrypt the Payload:
- Using the
CEK
, decrypt theJWE
ciphertext to extract the original payload.
- Using the
JWS
Verification
Understand the process of using JWS verification to confirm the integrity of data post-decryption, ensuring its trustworthiness.
After decryption, if the data includes a JWS
token, its integrity can be confirmed through verification. This step ensures that the data has not been altered and is indeed from a trusted source.
Using Signature in Header
We discuss the technique of signing HTTP requests by attaching a digital signature to the header, enabling servers to verify request authenticity and integrity.
The concept of signing involves creating a digital signature for the body of a HTTP
request. This digital signature is then included in the request headers, allowing the server to validate the request's authenticity and integrity.
- The Request Body: The sensitive data you want to protect.
RSA
-SHA256
Signature: A cryptographic hash of the request body, encrypted with a private key. This hash is sent to the server for verification.
Steps
Generate
RSA
Key Pair: You need aRSA
private key for signing and a corresponding public key for signature verificationFor each request
sign the Request: Calculate the
SHA256
hash of the request body, and encrypt the hash using your private keyAttach Signature to Header: Include your signature e.g. with a header name
Signature
orX-Payload-Signature
header when you send your HTTP request.Server-side Verification: The server uses the public key to verify the received signature and the integrity of the request body.
POST /transaction HTTP/1.1
Date: Fri, 11 Oct 2024 09:38:52 GMT
Host: api.dummy.com
Digest: SHA-512=tVL/etooLtB3qNVs693Ka1xpbsjgJvf8Xb/gprvktjYRnNPac9eEWw7XVrXxbfuLh45eLiGBLMEeepHvZ2cuvA==
Signature: keyId="aa2bd832-191e-4d2b-98d4-b0d4778844f9",created=1728639532,algorithm="hs2019",headers="(request-target) host digest (created)",signature="aAaII33mSJsfuXuTRJet5tJF6cQuYL77+b0gU1UZQMVjrz6jf1uNm5ixjN/CiId8Q5lTf9N308MH9ktSGWvzrtq67v73hG9PxEtCWuUR5tEiBdUk4Jt8xYszAuJYfGCqkFeoIiB8dbWNcTRRrZSAJAUPbSoY4eVyl0w91zvsr2wv8Gu3vo18XxHqtccOA0yHV4HVdJdbX1l0XLicTILTC2U27gxbpL8qsHZ1jgGByk/Wmm3KHmFtgEVGLVj3kbc712r5l54se4cSoQGst+rAy3xk//07x3icjLynObflLtpy3qqZgAR1L01xH1ISK7WCsy7G8e8ueQuGU5hAbir0lA=="
Content-Length: 91
Content-Type: application/json
{
"amount": "1500",
"currency": "USD",
"recipient": "Alice",
"nrn": "83120702450"
}
Conclusion
Reflecting on the information presented, this conclusion underscores the role of JWT
and related technologies in securing sensitive information across web communications.
I have come to an understanding that a JWT
is a format, a simple carrier format and can be used broader than only a token in the header.
JWT
and JWE
are part of the JOSE
(JSON Object Signing and Encryption) framework. JWT
is popular now, because Oauth2 and OpenID Connect uses the JWT
for formatting the access and ID tokens. And using that framework, helps us safeguards sensitive information
When an access or ID token is uses the JWT
format, it should not be used as an information repository. The JWT
format can benefit your application architecture, by formatting the HTTP
Request body.
However, there are other possibilities that keep the HTTP
Request Body human-readable, by only adding a signature HTTP
Headers.
Sources
A list of tools, references, and resources consulted while writing this article provides you with additional context and avenues for further exploration into this topic.
Tools to play with
- JOSE Generator
- Private and Public Key Generator
- Tool that helps you gain insight in creating a signature for a HTTP Request Body
Sources for this article
- belgianmobileid aka ITSME information about JOSE
- https://medium.com/@idenisko/understanding-the-jwe-token-a-practical-guide-942224f6a9b6
- https://developer.visa.com/pages/encryption_guide/jwe-jws
- https://auth0.com/docs/get-started/apis/configure-json-web-encryption
- https://openid.net/developers/certified-openid-connect-implementations/
- https://openid.net/specs/draft-jones-json-web-encryption-02.html
- https://jwt.io/introduction/
- https://oauth.net/2/
- https://dev.to/ariefwara/rest-api-body-payload-signature-2277
- https://hookdeck.com/webhooks/guides/how-to-implement-sha256-webhook-signature-verification
Content-Type: application/jwt
Content-Type: application/json
- A really nice read on how protecting data is used in Open Banking in Europe. Even design principles are listed
Subscribe to my newsletter
Read articles from Kristof Riebbels directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Kristof Riebbels
Kristof Riebbels
I am a backend developer from Belgium. My first language is C#. Mostly working on the .net tech stack. Powershell is the script language that I am most familiar with. I love automating stuff. Tools you work with should be tools that you like to work with :). Loving the devops scene as well. At the moment, my platform of choice is Azure, but looking at GitHub these days as well. I do have some experience with typescript. but that is not my strongest suit. Working with Rider and Resharper, so thanks Jetbrains for making great tools :)