HIGH insecure deserializationbearer tokens

Insecure Deserialization with Bearer Tokens

How Insecure Deserialization Manifests in Bearer Tokens

Bearer tokens are commonly used to convey claims between parties. While the token itself is usually a base64url‑encoded JSON (JWT) or an opaque string, some applications mistakenly treat the decoded payload as a serializable object and feed it into language‑specific deserialization routines. When this happens, an attacker who can craft a malicious token can trigger arbitrary code execution on the server.

Typical vulnerable code paths include:

  • Python applications that base64.b64decode(token) then pass the result to pickle.loads() or yaml.load(..., Loader=yaml.FullLoader) without restricting the loader.
  • Java services that decode a token and feed the bytes to ObjectInputStream.readObject() (often seen in legacy SSO integrations).
  • .NET APIs that use BinaryFormatter.Deserialize on the token payload after base64 decoding.
  • Node.js code that mistakenly uses vm.runInThisContext or eval on the decoded JSON string, which can lead to prototype pollution or code injection if the payload is not strictly validated.

Real‑world examples: CVE‑2015-7501 (Apache Commons Collections) showed how a deserialization gadget chain can be triggered via a crafted serialized object. When a Bearer token is used as the vehicle for that object, the attack becomes possible without any authentication step. Similarly, CVE‑2017-5638 (Apache Struts2) demonstrated OGNL expression injection via deserialized data; an attacker could embed a malicious OGNL expression in a token and achieve remote code execution on any endpoint that blindly deserializes the token.

Because Bearer tokens are often transmitted in the Authorization: Bearer <token> header, the attack surface is entirely unauthenticated, making insecure deserialization a critical risk for APIs that expose such endpoints.

Bearer Tokens-Specific Detection

Detecting insecure deserialization in Bearer tokens requires observing whether the server attempts to reconstruct objects from the token payload. middleBrick’s Input Validation check includes active probes that send specially crafted tokens designed to trigger known deserialization gadget chains.

When you submit a URL to middleBrick, the scanner:

  1. Extracts any Authorization: Bearer header pattern from the target’s responses.
  2. Generates a series of tokens where the payload is base64url‑encoded versions of known malicious serialized objects (e.g., Java Serializable gadgets, Python pickle payloads, .NET BinaryFormatter streams).
  3. Sends each token in a request to the endpoint and monitors for error messages, delayed responses, or side‑effects that indicate successful object reconstruction (such as unexpected file writes, network calls, or changes in response status).
  4. Correlates findings with the token’s format: if the endpoint accepts a JWT but the server still attempts to deserialize the payload, the finding is flagged as “Insecure Deserialization – Bearer Token”.

Because the scan is black‑box and requires no credentials, the test works even when the token is opaque to the scanner; the scanner simply varies the token value and watches for behavioural changes. middleBrick reports the finding with a severity rating, the exact token value that triggered the behavior, and remediation guidance pulled from the OWASP API Security Top 10 (category A8:2019 – Injection).

Example of a finding in the middleBrick dashboard:

Finding: Insecure Deserialization in Bearer Token
Severity: High
Endpoint: https://api.example.com/auth/validate
Details: Server responded with 500 Internal Error after receiving a token containing a Java serialized gadget (base64 of `rO0ABXQAVg==`). This indicates the server attempted ObjectInputStream.readObject() on the token payload.
Remediation: Avoid deserializing token payloads; use a verified JWT library and validate the signature before accessing claims.

Bearer Tokens-Specific Remediation

The safest approach is to never deserialize the token payload with language‑specific object deserialization mechanisms. Instead, treat the token as a string, verify its integrity and authenticity, then extract claims using a purpose‑built library.

Below are language‑specific examples that show the correct flow.

Node.js (jsonwebtoken)

const jwt = require('jsonwebtoken');
const SECRET = process.env.JWT_SECRET;

function verifyBearerToken(req, res, next) {
  const auth = req.headers.authorization;
  if (!auth || !auth.startsWith('Bearer ')) {
    return res.status(401).send('Missing or malformed token');
  }
  const token = auth.slice(7); // remove 'Bearer '
  try {
    const payload = jwt.verify(token, SECRET, { algorithms: ['HS256'] });
    // payload is a plain JSON object; no further deserialization needed
    req.user = payload;
    next();
  } catch (err) {
    return res.status(401).send('Invalid token');
  }
}

Python (PyJWT)

import jwt

SECRET = 'your-256-bit-secret'
ALGORITHMS = ['HS256']

def verify_bearer_token(token: str):
    if not token:
        raise jwt.exceptions.InvalidTokenError('Token missing')
    try:
        payload = jwt.decode(token, SECRET, algorithms=ALGORITHMS)
        # payload is a dict; no pickle or yaml load
        return payload
    except jwt.PyJWTError as exc:
        raise jwt.exceptions.InvalidTokenError(str(exc))

# Usage in a Flask view
# auth_header = request.headers.get('Authorization')
# if auth_header and auth_header.startswith('Bearer '):
#     token = auth_header.split()[1]
#     user = verify_bearer_token(token)

Java (auth0/java-jwt)

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;

private static final Algorithm ALG = Algorithm.HMAC256("secret");

public static java.util.Map<String, Object> verifyBearerToken(String token) {
    if (token == null || !token.startsWith("Bearer ")) {
        throw new IllegalArgumentException("Missing Bearer token");
    }
    String jwt = token.substring(7);
    try {
        com.auth0.jwt.interfaces.DecodedJWT verified = JWT.require(ALG)
                .build()
                .verify(jwt);
        return verified.getClaims(); // Map of claim names to values
    } catch (JWTVerificationException e) {
        throw new IllegalArgumentException("Invalid token", e);
    }
}

C# (System.IdentityModel.Tokens.Jwt)

using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Text;

private static readonly string Secret = "your-256-bit-secret";
private static readonly SymmetricSecurityKey Key =
    new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Secret));

public static JwtSecurityToken ValidateBearerToken(string token)
{
    if (string.IsNullOrWhiteSpace(token) || !token.StartsWith("Bearer "))
        throw new ArgumentException("Missing Bearer token");

    var jwtToken = token.Substring("Bearer ".Length);
    var validationParams = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = Key,
        ValidateIssuer = false,
        ValidateAudience = false,
        ValidateLifetime = true,
        ClockSkew = TimeSpan.FromMinutes(5)
    };

    var handler = new JwtSecurityTokenHandler();
    var principal = handler.ValidateToken(jwtToken, validationParams, out var validatedToken);
    // validatedToken is a JwtSecurityToken; Claims are accessible via principal.Claims
    return (JwtSecurityToken)validatedToken;
}

Key takeaways:

  • Never pass the decoded token to pickle.loads(), ObjectInputStream, BinaryFormatter, or similar APIs.
  • Always verify the token’s signature (or MAC) using a trusted key before accessing any claims.
  • Restrict accepted algorithms to a safe list (e.g., only HS256 or RS256).
  • If your system must carry complex data, place it in a separate, encrypted payload or a server‑side session store rather than inside the token.

Frequently Asked Questions

Can middleBrick detect insecure deserialization if the token is opaque and the server does not return detailed error messages?
Yes. middleBrick’s active probes send tokens containing known malicious serialized objects and observe side‑effects such as changes in response time, status code, or outgoing network requests. Even without verbose error messages, these behavioural anomalies allow the scanner to infer that the server attempted to deserialize the payload.
Is it safe to use a JWT library that claims to support "custom deserialization" for complex claim types?
Only if the library explicitly restricts deserialization to a safe, whitelisted set of types and performs integrity verification before any object reconstruction. Otherwise, custom deserialization re‑introduces the same risks as raw pickle or ObjectInputStream. Prefer keeping claims as simple JSON primitives (strings, numbers, booleans, arrays, objects) and avoid custom types altogether.