HIGH information disclosureadonisjsapi keys

Information Disclosure in Adonisjs with Api Keys

Information Disclosure in Adonisjs with Api Keys — how this specific combination creates or exposes the vulnerability

Information Disclosure in AdonisJS when using API keys typically arises from insecure exposure or logging of keys, overly verbose error messages, and misconfigured route guards. AdonisJS applications often manage API keys via environment variables and request headers, but if these are handled carelessly, keys can leak through logs, client-side error responses, or debug output. For example, returning full key values in JSON error responses or stack traces can expose secrets to unauthorized parties.

Consider an AdonisJS route that authenticates requests using an API key header without proper validation and error handling:

// routes.ts
import Route from '@ioc:Adonis/Core/Route'

Route.get('/secure', async ({ request, response }) => {
  const apiKey = request.header('x-api-key')
  if (!apiKey) {
    return response.status(401).json({ error: 'Missing API key' })
  }
  // Insecure comparison or logging can lead to disclosure
  if (apiKey !== process.env.API_KEY) {
    return response.status(403).json({ error: 'Invalid API key', provided: apiKey })
  }
  response.json({ data: 'sensitive information' })
})

In the snippet above, returning the provided key in the response body (e.g., { provided: apiKey }) is a direct path to information disclosure. An attacker who receives this response learns the exact value they sent, which may be a valid key or a pattern useful for further attacks. Additionally, if AdonisJS’s built-in logger or development error pages are active in production, stack traces or debug logs might inadvertently include the key from headers, environment, or configuration files.

Another common pattern is using API keys for service-to-service calls where the key is passed through query parameters or URLs. AdonisJS code that constructs redirect URLs or external requests with keys in query strings can expose keys in server logs, browser history, or referrer headers:

// Example of insecure external request with key in URL
import { axios } from '@ioc:Adonis/Core/Helpers'

export async function callExternalService(apiKey: string) {
  // Risk: key exposed in logs and server-side referrers
  const url = `https://external.example.com/data?api_key=${apiKey}`
  return axios.get(url)
}

Furthermore, if route metadata or OpenAPI specs generated by AdonisJS tools inadvertently include API key examples or values (for instance, via @mockoon or custom documentation generators), those artifacts can be exposed through documentation endpoints. A development-mode endpoint like /docs that renders request examples containing hardcoded keys can become an accidental disclosure channel.

To summarize the specific risks in this combination:

  • Keys echoed in JSON error responses or logs.
  • Keys exposed via query strings or URL fragments that are recorded in logs.
  • Keys present in debug or exception output due to verbose error configurations.
  • Keys inadvertently included in generated documentation or mocks.

Api Keys-Specific Remediation in Adonisjs — concrete code fixes

Remediation focuses on preventing the exposure of API key values in responses, logs, and URLs, and ensuring secure handling and comparison. Below are concrete, secure patterns for AdonisJS.

1. Avoid returning the key in responses

Never echo the key back to the client, even for debugging. Use generic error messages and log securely instead.

// routes.ts — secure error handling
import Route from '@ioc:Adonis/Core/Route'

Route.get('/secure', async ({ request, response, logger }) => {
  const apiKey = request.header('x-api-key')
  if (!apiKey) {
    return response.status(401).json({ error: 'Unauthorized' })
  }
  if (!apiKeyIsValid(apiKey)) {
    // Log the event without the key value
    logger.warning('Invalid API key attempt', { userId: request.auth?.user?.id ?? 'unknown' })
    return response.status(403).json({ error: 'Forbidden' })
  }
  response.json({ data: 'sensitive information' })
})

2. Use constant-time comparison to prevent timing leaks

Avoid simple !== comparison which can be vulnerable to timing attacks. Use a secure compare function.

// utils/security.ts
import { timingSafeEqual } from 'node:crypto'

export function apiKeyIsValid(provided: string): boolean {
  const expected = Buffer.from(process.env.API_KEY || '')
  const providedBuf = Buffer.from(provided)
  // Length mismatch short-circuit to avoid DoS via long inputs
  if (expected.length !== providedBuf.length) return false
  return timingSafeEqual(expected, providedBuf)
}

3. Avoid keys in URLs and query parameters

When calling external services, use headers instead of query strings for keys, and avoid constructing URLs that embed secrets.

// services/external.ts — secure outbound call
import { axios } from '@ioc:Adonis/Core/Helpers'

export async function callExternalService(apiKey: string) {
  return axios.get('https://external.example.com/data', {
    headers: { 'x-api-key': apiKey }
  })
}

4. Secure environment and configuration access

Ensure API keys are read from environment variables and not hardcoded. Use AdonisJS’s config system to restrict exposure.

// start/hooks.ts — safe config loading
import { Env } from '@ioc:Adonis/Core/Env'

const apiKey = Env.get('API_KEY')
if (!apiKey) {
  throw new Error('API_KEY is not defined in environment')
}

// Do not log or serialize apiKey

5. Control logging and error verbosity

In production, disable detailed error stacks and ensure logs do not capture sensitive headers. Use structured logging with filters if necessary.

// .env
APP_ENV=production
LOG_LEVEL=warn
# Ensure no middleware logs full headers containing API keys

6. Protect generated documentation

If generating OpenAPI specs or mock documentation, ensure no real keys appear in examples. Use placeholder values and enforce via schema validation.

// Example of safe schema example (never use real keys)
/**
 * @oas get /secure
 * parameters:
 *   - name: x-api-key
 *     in: header
 *     description: API key for authentication
 *     example: placeholder-key-123
 */

Frequently Asked Questions

Why is returning the API key in an error response a disclosure risk?
Returning the API key in a response body or error object exposes the secret to anyone who can trigger or observe the error. Attackers can use these echoes to confirm guessed keys or build valid keys, bypassing authentication.
How does using timingSafeEqual mitigate key comparison risks?
TimingSafeEqual performs a constant-time comparison so an attacker cannot infer key validity based on response timing. Simple !== comparisons may short-circuit on prefix matches, allowing timing-based side-channel attacks to gradually reveal a valid key.