HIGH insecure deserializationhapiapi keys

Insecure Deserialization in Hapi with Api Keys

Insecure Deserialization in Hapi with Api Keys — how this specific combination creates or exposes the vulnerability

Insecure deserialization occurs when an application processes untrusted serialized data without sufficient validation, allowing an attacker to manipulate object graphs and execute arbitrary code or bypass authorization checks. In a Hapi application that uses API keys for authentication, combining weak deserialization logic with key-based access controls can amplify the impact of a deserialization flaw.

Consider a Hapi endpoint that accepts a serialized payload (e.g., via a POST body or a signed cookie) and deserializes it before checking the caller’s API key. If the deserialization runs before the API key is validated, an attacker can craft a malicious serialized object that elevates privileges or triggers server-side actions. Even when the API key is checked afterward, the damage may already be done because object instantiation can invoke constructors, setters, or prototype manipulations during deserialization.

For example, a Hapi route that uses Node.js built-in serialize/deserialize or third-party libraries like node-serialize can be tricked into executing functions during deserialization. If the API key is passed as a header and only verified after deserialization, the attacker’s payload can exploit prototype pollution to overwrite admin-level permissions or to call functions that perform filesystem or network operations. Real-world patterns include gadget chains involving built-in objects such as RegExp and Function, which can lead to remote code execution under certain conditions.

SSRF and data exposure risks also emerge when deserialized objects perform network requests or access sensitive resources. An attacker might embed URLs or file paths inside the serialized data, causing the server to reach internal services or leak files after the API key check is bypassed via tampered claims. Because the deserialization happens early in the request lifecycle, logging and rate limiting might still record the request as valid, complicating incident response.

Insecure deserialization with API keys is particularly dangerous when the deserialization step is spread across multiple plugins or when custom serializers are used. The API key may appear to protect the endpoint, but if trust is placed in deserialized data before validation, the control is effectively weakened. Testing should include sending manipulated serialized blobs and observing whether server-side logic, file reads, or outbound connections are triggered before the API key validation logic executes.

Api Keys-Specific Remediation in Hapi — concrete code fixes

To mitigate insecure deserialization in Hapi when using API keys, ensure that deserialization occurs only after strict validation of the API key and that deserialization does not execute untrusted code. The following patterns demonstrate secure handling.

1. Validate API key before any deserialization

Check the API key immediately in a server extension or route pre-handler so that malicious payloads are rejected before any object instantiation.

const Hapi = require('@hapi/hapi');

const validApiKeys = new Set(['abc123', 'trusted-key-xyz']);

const server = Hapi.server({ port: 4000 });

server.ext('onPreHandler', async (request, h) => {
  const apiKey = request.headers['x-api-key'];
  if (!apiKey || !validApiKeys.has(apiKey)) {
    return h.response({ error: 'Unauthorized' }).code(401);
  }
  return h.continue;
});

server.route({
  method: 'POST',
  path: '/safe-endpoint',
  handler: (request, h) => {
    // At this point, API key has been validated
    const payload = request.payload; // Ensure you use a safe parser for JSON only
    return { received: payload };
  }
});

server.start();

2. Avoid dangerous deserialization libraries; prefer strict parsing

Do not use functions that execute code during deserialization. If you must handle serialized data, restrict to JSON and use JSON.parse with a reviver that does not invoke setters or constructors unexpectedly.

server.route({
  method: 'POST',
  path: '/deserialize-safe',
  options: {
    parse: {
      // Limit to JSON payloads only
      payload: {
        output: 'data',
        parse: true,
        allow: 'application/json'
      }
    }
  },
  handler: (request, h) => {
    // request.payload is already a plain object from JSON.parse
    const data = request.payload;
    // Perform additional schema validation here
    if (!data || typeof data !== 'object' || Array.isArray(data)) {
      return h.response({ error: 'Invalid payload' }).code(400);
    }
    return { safe: true, data };
  }
});

3. Explicitly reject prototype pollution vectors

Sanitize incoming objects for dangerous properties such as __proto__, constructor, and prototype before any further processing.

function sanitize(obj) {
  if (obj && typeof obj === 'object') {
    delete obj.__proto__;
    delete obj.constructor;
    delete obj.prototype;
    for (const key of Object.keys(obj)) {
      if (typeof obj[key] === 'object') {
        sanitize(obj[key]);
      }
    }
  }
  return obj;
}

server.route({
  method: 'POST',
  path: '/sanitized',
  handler: (request, h) => {
    const clean = sanitize({ ...request.payload });
    return { sanitized: clean };
  }
});

4. Enforce schema validation with a library like Joi

Define strict shapes for expected payloads to prevent unexpected properties that could be abused during deserialization.

const Joi = require('joi');

const schema = Joi.object({
  action: Joi.string().valid('create', 'update').required(),
  resourceId: Joi.number().integer().positive().required()
});

server.route({
  method: 'POST',
  path: '/validated',
  handler: (request, h) => {
    const { error, value } = schema.validate(request.payload);
    if (error) {
      return h.response({ error: error.details[0].message }).code(400);
    }
    return { valid: value };
  }
});

5. Apply principle of least privilege to API keys

Scope API keys to specific endpoints and operations so that even if deserialization is triggered, the attacker’s impact is limited. Rotate keys regularly and avoid embedding secrets in client-side code.

Frequently Asked Questions

Can I safely deserialize user input in Hapi if I validate the API key first?
Validating the API key before deserialization reduces risk, but you should still avoid unsafe deserialization of untrusted data. Prefer strict parsing (e.g., JSON only) and schema validation instead of generic deserialization functions that may execute code.
What should I do if my Hapi app must handle serialized data from third parties?
If you must accept serialized formats, use a sandboxed parser or strict schema validation and disable code execution paths. Reject inputs containing dangerous prototypes or constructors, and ensure API key checks happen before processing, with limited scopes to minimize impact.