Dns Cache Poisoning in Adonisjs with Jwt Tokens
Dns Cache Poisoning in Adonisjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability
DNS cache poisoning and JWT token handling intersect in AdonisJS applications when network-dependent operations—such as resolving authentication endpoints, fetching JWKS (JSON Web Key Set) for key rotation, or validating issuer URLs—are performed using user-influenced data. If an AdonisJS app resolves hostnames dynamically (e.g., via require('dns').promises.lookup' or an HTTP client configured with a hostname derived from request input), an attacker who can poison the DNS cache may redirect these calls to a malicious host. This can cause the app to retrieve a malicious or tampered public key, validate tokens with an incorrect algorithm, or communicate with a rogue OAuth/OIDC provider.
Specifically with JWT tokens, common patterns that introduce risk include:
- Using environment variables or configuration values that include hostnames (e.g.,
JWKS_URI=https://auth.example.com/.well-known/jwks.json) where the hostname part may be overridden or constructed from request parameters, subdomains, or tenant identifiers. - Performing dynamic endpoint discovery via DNS lookups as part of authentication middleware, which may be chained with JWT verification steps.
If the DNS resolution for these endpoints is poisoned, the application may unknowingly use attacker-controlled keys or endpoints, bypassing signature validation or enabling token substitution. This is especially relevant when the app does not pin public keys or enforce strict issuer validation. Even if the JWT library enforces iss and aud checks, a poisoned DNS lookup can present a malicious certificate or JWKS that still appears to be issued by a trusted authority, depending on how the trust chain is configured.
Moreover, if the AdonisJS application runs in an environment where DNS caching is shared or predictable (e.g., containerized environments or OS-level caches), the window for exploitation may be extended. An attacker does not need to compromise the application server itself; they need only influence DNS resolution for the specific hostnames used during JWT validation. This makes the combination of dynamic hostname resolution and JWT token validation a notable attack surface.
To determine whether your AdonisJS app is at risk, middleBrick can scan the endpoint handling authentication and token validation. The scan tests unauthenticated attack surface areas, including input validation, authentication mechanisms, and data exposure checks, without requiring credentials or agents. You can run a free scan to receive a security risk score and prioritized findings with remediation guidance.
Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on removing dynamic DNS dependencies for JWT validation and enforcing strict, static configuration of trusted endpoints and keys. The following practices and code examples demonstrate how to secure JWT handling in AdonisJS.
1. Use static JWKS URLs and pin keys
Avoid constructing JWKS URIs from user input or environment variables that can be influenced. Instead, use a hardcoded or securely managed URI and, where possible, pin the expected public key or key fingerprint.
// config/jwt.ts
import { type HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { schema } from '@ioc:Adonis/Core/Validator'
export default {
providers: [
'jwt-provider'
],
config: {
issuer: 'https://auth.example.com',
audience: 'api.example.com',
// Use a static, pinned JWKS URI
jwksUri: 'https://auth.example.com/.well-known/jwks.json',
// Optionally pin a specific public key (pem format)
publicKey: `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo
...
-----END PUBLIC KEY-----`
}
}
2. Validate issuer and audience strictly
Ensure that token validation checks both iss (issuer) and aud (audience) against fixed values. Do not allow these claims to be overridden by token contents in a way that could be influenced by DNS or endpoint changes.
// app/Providers/JwtProvider.ts
import { BaseProvider } from '@ioc:Adonis/Core/Provider'
import { Jwt } from '@poppinss/auth'
export default class JwtProvider extends BaseProvider {
public register() {
this.container.singleton('jwt', () => {
return new Jwt({
issuer: 'https://auth.example.com',
audience: 'api.example.com',
secret: process.env.JWT_SECRET || '',
// For asymmetric keys, specify algorithm explicitly
algorithm: 'RS256'
})
})
}
}
3. Avoid dynamic hostname resolution for auth endpoints
If you must use dynamic subdomains or tenant-aware hostnames, resolve them at startup or via secure configuration—not per-request—and validate against a strict allowlist. Do not perform DNS lookups as part of request-time JWT validation.
// Example: safe tenant resolution at startup
const allowedTenants = ['app', 'admin', 'portal']
function resolveTenantHost(host: string): string | null {
const tenant = host.split('.')[0]
return allowedTenants.includes(tenant) ? host : null
}
// In a route middleware
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default async function tenantJwtMiddleware({ request, response, next }: HttpContextContract) {
const host = request.hostname()
const resolved = resolveTenantHost(host)
if (!resolved) {
return response.unauthorized('Invalid tenant')
}
await next()
}
4. Use middleware that enforces strict validation
AdonisJS allows you to define route middleware that verifies JWTs with fixed parameters. Avoid middleware that re-resolves endpoints or keys on each request.
// start/kernel.ts
import { Exception } from '@poppinss/utils'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export const jwt = async (ctx: HttpContextContract) => {
const jwt = ctx.container.use('jwt')
try {
const payload = await jwt.verify(ctx.request.header('authorization')?.replace('Bearer ', ''))
ctx.auth.user = payload
} catch (error) {
throw Exception.unauthorized('Invalid token')
}
}
5. Use the CLI to audit and automate checks
You can integrate scanning into your workflow using the middlebrick CLI to detect risky configurations and dependencies. For example:
# Install the package
npm install -g middlebrick
# Scan an endpoint handling JWT authentication
middlebrick scan https://api.example.com/auth/login
The CLI returns a JSON or text report with a security risk score and prioritized findings, including input validation and authentication checks relevant to JWT handling. For teams, the Pro plan adds continuous monitoring and GitHub Action integration to fail builds if the risk score drops below your threshold.
By combining static configuration, strict claim validation, and avoidance of runtime DNS dependencies, you reduce the attack surface related to DNS cache poisoning while maintaining robust JWT security in AdonisJS.