HIGH api key exposurefeathersjsredis

Api Key Exposure in Feathersjs with Redis

Api Key Exposure in Feathersjs with Redis — how this specific combination creates or exposes the vulnerability

FeathersJS is a framework for building REST and real-time APIs with JavaScript/TypeScript. When it integrates with Redis—commonly used for caching, session storage, or message brokering—misconfigured services can unintentionally expose API keys. This exposure typically occurs when keys are stored as Redis string values or hashes and are returned to unauthenticated or low-privilege API consumers through overly broad service queries or misconfigured hooks.

In a FeathersJS application, an API key might be stored in Redis with a key such as apikey:provider:external. If a Feathers service lacks proper authentication or authorization checks and retrieves this value based on user-supplied parameters (e.g., a query for provider), an attacker can enumerate or infer valid keys. For example, an endpoint that proxies requests to a third-party system may read the key from Redis and forward it in headers; if that endpoint does not validate the caller’s scope or identity, the key can be reflected in responses or logged insecurely.

Another vector involves Redis data types. A hash storing metadata about integrations, such as integration:123 with a field api_key, can be read via the FeathersJS Redis client if the service iterates over entries without filtering by access control. Real-world attack patterns include probing for predictable key names (e.g., namespaced under apikey:) or exploiting verbose error messages that reveal key existence or structure. These patterns align with common OWASP API Top 10 risks like Broken Object Level Authorization (BOLA) and Security Misconfiguration, and they can be surfaced by middleBrick scans that correlate OpenAPI specs with runtime behavior.

SSRF and unsafe consumption further amplify the risk. If a FeathersJS service accepts user-supplied URLs or hostnames and uses them to query Redis (e.g., to look up credentials for a downstream service), an attacker can point the lookup toward internal Redis instances or administrative interfaces. Should the Redis instance allow connections without strong network isolation, an API key intended for external services might be retrieved and returned to the caller. middleBrick’s checks for Redis-specific exposure, SSRF, and Unsafe Consumption are designed to detect such configurations by cross-referencing spec definitions with observed responses.

Encryption and Data Exposure checks are relevant when API keys are stored without encryption at rest in Redis. While Redis does not natively encrypt data on disk, application-layer encryption should be used for highly sensitive values; otherwise, keys may be readable to anyone who can access the Redis dataset. middleBrick evaluates whether findings like these map to compliance frameworks such as PCI-DSS and SOC2, emphasizing the need to treat API keys as high-sensitivity data regardless of storage layer.

Redis-Specific Remediation in Feathersjs — concrete code fixes

Remediation focuses on strict access control, data minimization, and avoiding direct exposure of keys in API responses. In FeathersJS, ensure that services which interact with Redis enforce authentication and scoped authorization before any Redis operation. Do not return raw Redis values in REST or real-time responses; instead, map them to safe data transfer objects.

Use namespaced keys with unpredictable suffixes and avoid storing API keys in plaintext hashes where possible. If keys must be stored, treat them as secrets and limit which services and users can read them. The following examples illustrate secure patterns using the redis client in a FeathersJS service.

Example 1: Secure key retrieval with scope validation

Instead of a broad lookup, fetch only the key required for the authenticated context and do not echo it back to the client.

const redis = require('redis');
const client = redis.createClient({ url: process.env.REDIS_URL });

class ApiKeyService {
  constructor(feathersApp) {
    this.app = feathersApp;
  }

  async get(req, provider) {
    // Ensure req.user is authenticated and has a scoped role
    if (!req.user || !req.user.scopes.includes('keys:read')) {
      throw new Error('Unauthorized');
    }

    // Use a deterministic but scoped key; avoid exposing the raw key in responses
    const keyName = `apikey:${provider}:${req.user.orgId}`;
    const key = await client.get(keyName);

    if (!key) {
      throw new Error('Key not found');
    }

    // Return only a masked representation or use it internally
    return { provider, maskedKey: key.slice(0, 4) + '****' + key.slice(-4) };
  }
}

module.exports = function (app) {
  app.use('/api-key', new ApiKeyService(app));
};

Example 2: Parameterized hash access with field-level permissions

Store keys in hashes and restrict which fields can be read based on caller roles.

const redis = require('redis');
const client = redis.createClient({ url: process.env.REDIS_URL });

class IntegrationService {
  constructor(feathersApp) {
    this.app = feathersApp;
  }

  async find(params) {
    const user = params.user;
    // Only allow reading the 'api_key' field if user has 'secrets:read' scope
    const canReadSecret = user.scopes.includes('secrets:read');

    const entries = await client.hGetAll('integration:123');
    const result = {
      id: '123',
      name: entries.name,
      ...(canReadSecret ? { api_key: entries.api_key } : {})
    };
    return result;
  }
}

module.exports = function (app) {
  app.use('/integrations', new IntegrationService(app));
};

Example 3: Avoid using user input as Redis keys without validation

Prevent key injection by validating and normalizing identifiers before constructing Redis keys.

const redis = require('redis');
const client = redis.createClient({ url: process.env.REDIS_URL });

function safeKey(provider, userOrg) {
  // Allow only alphanumeric provider names and org IDs
  if (!/^[a-z0-9_-]+$/.test(provider) || !/^[a-z0-9_-]+$/.test(userOrg)) {
    throw new Error('Invalid identifier');
  }
  return `apikey:${provider}:${userOrg}`;
}

// Usage in a Feathers hook
app.hooks({
  before: {
    async find(context) {
      const keyName = safeKey(context.params.query.provider, context.params.user.orgId);
      const key = await client.get(keyName);
      context.result.data.key = key; // handled internally, not exposed
    }
  }
});

These examples emphasize least privilege, separation of duties, and avoiding reflection of secrets. middleBrick’s Continuous Monitoring in the Pro plan can help detect regressions by scanning on a configurable schedule, and the GitHub Action can fail builds if risk scores drop below your defined threshold. For ad hoc analysis, the CLI tool supports middlebrick scan <url> to validate remediation without requiring changes to your application code.

Frequently Asked Questions

How can I verify that my API keys stored in Redis are not being exposed through FeathersJS endpoints?
Use automated scanning that correlates spec definitions with runtime behavior. middleBrick’s scans test unauthenticated attack surfaces and can surface BOLA, Data Exposure, and SSRF findings that indicate potential key leakage. Validate that services never return raw Redis values and enforce scoped authorization before any Redis operation.
Does middleBrick provide guidance on encrypting API keys in Redis for FeathersJS applications?
middleBrick evaluates Encryption and Data Exposure findings and maps them to compliance frameworks. It does not provide step-by-step encryption implementation, but its remediation guidance recommends application-layer encryption for highly sensitive values and highlights checks aligned with OWASP API Top 10 and PCI-DSS considerations.