HIGH ssrf server sideadonisjsjwt tokens

Ssrf Server Side in Adonisjs with Jwt Tokens

Ssrf Server Side in Adonisjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability

Server Side Request Forgery (SSRF) in an AdonisJS application that uses JWT tokens for authentication can occur when an endpoint that validates and acts upon a JWT also performs arbitrary HTTP requests based on attacker-controlled input. Because the JWT carries identity and potentially elevated scopes, the backend may trust input that originates from an authenticated context and initiate requests to internal or external hosts that would otherwise be unreachable. Typical patterns include a user-supplied URL or host header being passed to an HTTP client after the JWT is verified, allowing an authenticated user to direct the server to interact with internal metadata services, cloud instance metadata endpoints, or internal APIs that trust the loopback interface.

In AdonisJS, this often maps to routes guarded by JWT middleware where the route handler parses the token and then uses parameters such as url, host, or file to perform outbound calls. If input validation is limited to presence checks and the JWT is assumed to imply safety, an authenticated attacker can abuse this to perform SSRF against internal services, bypassing firewall rules that would normally block unauthenticated access. For example, an endpoint meant to proxy a report or fetch a user avatar can be coerced into probing the metadata service at http://169.254.169.254 or internal Kubernetes endpoints when the JWT identifies an admin or service account.

The risk is compounded when the JWT contains scopes or roles that authorize sensitive operations, and the application logic does not enforce additional checks on the target of the outbound request. AdonisJS does not inherently prevent SSRF; the framework relies on developer-side validation and network controls. Without strict allowlists, hostname resolution controls, and network segmentation, an SSRF enabled by a trusted JWT-authenticated session can lead to internal service enumeration, cloud metadata exfiltration, or pivot into adjacent environments.

Middleware that verifies JWTs and attaches user context to the request should never skip validation of outbound destinations. Even with a valid token, the application must treat the request body, query parameters, and headers as untrusted. Combining JWT-based authorization with unchecked HTTP client configuration creates a pathway where authenticated requests can be weaponized to interact with unintended internal endpoints, illustrating why SSRF considerations must be applied uniformly regardless of authentication state.

Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes

To mitigate SSRF in AdonisJS when JWT tokens are used, enforce strict input validation and network controls independently of authentication. Treat validated JWT claims as identity, not as authorization to access arbitrary resources. Apply allowlists for destinations, disable unnecessary protocols, and avoid forwarding user-controlled data directly to outbound HTTP clients.

Example JWT middleware and route handling in AdonisJS (with remediation)

Use AdonisJS middleware to verify JWTs and attach a user payload, but ensure that any user-controlled input used for outbound requests is validated against a strict allowlist.

// start/kernel.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { schema, rules } from '@ioc:Adonis/Core/Validator'

// Example route-specific validator
const reportSchema = schema.create({
  url: stringSchema({}, [
    rules.url({ allowedProtocols: ['https'] }), // restrict to HTTPS
    rules.hostname({ allowPrivate: false }),    // prevent private IPs/localhost
    rules.regex({ pattern: /^https:\/\/reports\.example\.com\/v1\/.+/ }), // strict prefix allowlist
  ]),
  token: stringSchema({}, [rules.alpha()]),    // non-opaque example constraint
})

export default class ReportsController {
  public async showReport({ request, response, auth }: HttpContextContract) {
    const payload = request.validate({ schema: reportSchema })
    const user = await auth.authenticate()      // JWT verified by auth provider

    // Ensure the JWT scopes authorize this operation
    if (!user.scopes.includes('reports:fetch')) {
      return response.forbidden({ message: 'Insufficient scope' })
    }

    // Safe outbound call with explicit whitelisting
    const safeUrl = new URL(payload.url)
    if (!safeUrl.hostname.endsWith('.example.com')) {
      return response.badRequest({ message: 'Destination not allowed' })
    }

    // Use a configured HTTP client with disabled protocols like file:// and gopher://
    const ReportHttp = use('App/Services/ReportHttp')
    const data = await ReportHttp.get(safeUrl.toString(), {
      headers: { Authorization: `Bearer ${user.token}` },
    })

    return response.ok(data)
  }
}

Configure the HTTP client to restrict protocols and hosts. For example, using got or axios with explicit settings:

// app/Services/ReportHttp.ts
import got from 'got'

class ReportHttp {
  async get(url: string, options = {}) {
    return got(url, {
      protocol: 'https:',
      hostname: new URL(url).hostname,
      // Disable protocols that should never be used
      handlers: {
        // Ensure only HTTPS is used; reject file://, data:, gopher://
      },
      // Timeouts and redirects control
      timeout: { request: 5000 },
      followRedirect: false, // or strict redirect allowlist
      ...options,
    }).json()
  }
}

export default new ReportHttp()

In addition to code controls, apply network-level mitigations such as egress filtering and deny-list updates to block access to known internal IP ranges from application containers. Combine JWT validation with these measures so that possessing a valid token does not extend the attack surface for SSRF.

Minimal JWT example with hostname checks

When decoding and using JWT claims, validate the token and then enforce destination rules before any outbound call.

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { createRemoteJwtClient } from 'some-jwt-library'

export default class ProxyController {
  public async proxyAsset({ request, response, auth }: HttpContextContract) {
    const { assetId } = request.qs()
    const user = await auth.authenticate()

    // Map assetId to a known domain allowlist
    const allowedDomains = ['assets.example.com', 'cdn.example.com']
    const targetDomain = this.mapAssetToDomain(assetId)

    if (!allowedDomains.includes(targetDomain)) {
      return response.badRequest({ message: 'Asset domain not allowed' })
    }

    const client = createRemoteJwtClient({ baseUrl: `https://${targetDomain}` })
    const data = await client.get(`/v1/assets/${assetId}`, {
      headers: { Authorization: `Bearer ${user.token}` },
    })

    return response.ok(data)
  }

  private mapAssetToDomain(assetId: string): string {
    // deterministic mapping, no user input in domain selection
    return assetId.startsWith('img') ? 'assets.example.com' : 'cdn.example.com'
  }
}

Frequently Asked Questions

Does a valid JWT reduce SSRF risk in AdonisJS?
No. A valid JWT identifies and authorizes a user but does not reduce SSRF risk. SSRF mitigation must be applied independently via strict destination allowlists, protocol restrictions, and network controls regardless of authentication state.
How can I test whether my AdonisJS endpoints are vulnerable to SSRF when authenticated?
Use black-box testing with authenticated probes that supply attacker-controlled URLs or hosts in requests protected by JWT middleware. Observe whether the backend follows internal or private network endpoints; combine this with spec analysis to ensure runtime behavior matches intended allowlists.