Zone Transfer in Adonisjs with Api Keys
Zone Transfer in Adonisjs with Api Keys — how this specific combination creates or exposes the vulnerability
A zone transfer in the context of DNS refers to the replication of DNS records between DNS servers. When a DNS client requests a zone transfer, the DNS server may respond with its full zone data, including hostnames and IPs. In AdonisJS applications, this typically manifests when the framework or an underlying dependency performs DNS resolution for service discovery, configuration, or integration purposes.
Using API keys in AdonisJS introduces a specific risk when DNS-based service discovery is involved. If the application resolves internal service hostnames via DNS and those resolutions are performed without proper validation, an attacker who can influence the DNS response may redirect the application to a malicious server. The API key—often passed in headers or environment variables—may then be presented to this rogue server, leading to credential leakage or unauthorized actions. This becomes a zone-transfer-like exposure when internal DNS zones inadvertently allow recursive or unauthorized transfers, enabling an attacker to enumerate internal hostnames and infer service topology.
Furthermore, if the AdonisJS application uses a package that performs unauthenticated DNS queries to discover backend services, and those queries are made in the context of requests where API keys are used for authorization, the combination can expose internal endpoints. For example, a misconfigured DNS server may respond to a zone transfer request with full records, revealing internal service names and IPs that should remain hidden. The API key, used as a bearer token for downstream service communication, can then be targeted via SSRF or DNS rebinding if the application does not validate endpoint origins.
Real-world patterns include dependencies such as @adonisjs/fold for IoC container resolution or custom service locators that perform DNS lookups. If these lookups are not constrained to expected endpoints and the application passes API keys to the resolved host, the risk of unintended data exposure increases. Attackers may leverage open DNS resolvers to conduct a zone transfer, gather internal infrastructure details, and then attempt to interact with AdonisJS services using captured API keys.
Api Keys-Specific Remediation in Adonisjs — concrete code fixes
To mitigate risks associated with API keys in AdonisJS, focus on preventing unintended DNS interactions and ensuring API keys are never exposed to untrusted endpoints. Below are concrete remediation steps with code examples tailored to AdonisJS.
1. Restrict DNS resolution and avoid dynamic host resolution
Do not perform DNS lookups based on user input or external configuration. Instead, use static, validated endpoints. If you must resolve hostnames, validate them against an allowlist.
// services/DnsValidator.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class DnsValidator {
private allowedHosts = new Set([
'api.internal.example.com',
'auth.service.example.com'
])
public validateHost(host: string): boolean {
return this.allowedHosts.has(host)
}
}
// In a controller or service
import DnsValidator from 'App/Services/DnsValidator'
export default class SomeController {
public async store({ request }: HttpContextContract) {
const host = request.input('host')
const validator = new DnsValidator()
if (!validator.validateHost(host)) {
return response.badRequest({ error: 'Invalid host' })
}
// Proceed with trusted host
}
}
2. Secure API key usage with middleware and environment isolation
Ensure API keys are not passed to dynamically resolved hosts. Use AdonisJS middleware to inject keys only to trusted services and avoid logging them.
// start/hooks.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export const globalMiddleware = [
'App/Middleware/VerifyApiKeyTarget'
]
// middleware/VerifyApiKeyTarget.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class VerifyApiKeyTarget {
public async handle({ request, response, auth }: HttpContextContract, next: () => Promise) {
const apiKey = request.header('X-API-Key')
const targetHost = request.url().split('://')[1]?.split('/')[0]
// Ensure targetHost is in allowlist
const allowedHosts = ['api.internal.example.com']
if (!allowedHosts.includes(targetHost || '')) {
return response.forbidden({ error: 'Target host not allowed' })
}
// Optionally, redact API key from logs
request.logextras = { maskedApiKey: apiKey ? apiKey.slice(0, 4) + '****' : 'none' }
await next()
}
}
3. Use HTTP client configuration with fixed endpoints
When making outbound requests, configure the HTTP client with fixed base URLs and avoid concatenating user input into URLs.
// start/HttpClient.ts
import { BaseHttpClient } from '@ioc:Adonis/Core/HttpClient'
export class SecureHttpClient {
private client: BaseHttpClient
constructor() {
this.client = new BaseHttpClient({
baseURL: 'https://api.internal.example.com',
timeout: 5000
})
}
public async getProtected(path: string, apiKey: string) {
// Do not allow dynamic baseURL from input
return this.client.get(path, {
headers: {
'X-API-Key': apiKey,
'Content-Type': 'application/json'
}
})
}
}
// Usage in a controller
import SecureHttpClient from 'App/HttpClient'
export default class ResourceController {
public async index() {
const client = new SecureHttpClient()
const response = await client.getProtected('/data', process.env.API_KEY as string)
return response.json
}
}
4. Environment and secret management
Ensure API keys are stored in environment variables and never committed to version control. Use AdonisJS dotenv support and validate required variables at startup.
// .env
API_KEY=super-secret-key-not-to-be-exposed
ALLOWED_HOSTS=api.internal.example.com,auth.service.example.com
// start/app.ts
import Env from '@ioc:Adonis/Core/Env'
const allowedHosts = Env.get('ALLOWED_HOSTS', '').split(',')
if (allowedHosts.length === 0) {
throw new Error('ALLOWED_HOSTS must be defined in .env')
}
// Optionally validate at boot
console.log('API service initialized with restricted hosts')