HIGH ssrfjwt tokens

Ssrf with Jwt Tokens

How Ssrf Manifests in Jwt Tokens

Server-Side Request Forgery (SSRF) can intersect with JSON Web Tokens (JWTs) when an application uses an HTTP request to validate or exchange a JWT, and that request is constructed from attacker-controlled data. Because JWTs are often transmitted in Authorization headers, a vulnerable endpoint that builds a request to a introspection or validation endpoint can be abused to force the server to fetch internal resources, including metadata services that issue JWT signing keys.

One common pattern is an OAuth2 or OpenID Connect dynamic client registration or token introspection flow where the client passes an issuer URL or JWKS URI. If the application directly uses a user-supplied parameter to form the request without strict allowlisting, an attacker can supply an internal address such as http://169.254.169.254/latest/meta-data/iam/security-credentials/ (AWS instance metadata). The server resolves this and may return credentials that can be used to forge JWTs.

Another JWT-specific path involves backend services that fetch remote public keys to verify signatures. Consider a Node.js service that uses jwks-rsa and allows the jwksUri to be configured per tenant. If the URI is derived from a query parameter without validation, an attacker can set it to an internal service that returns a valid key but also exfiltrates data:

const jwksClient = require('jwks-rsa');
const client = jwksClient({
  jwksUri: req.query.jwksUri || 'https://default.example.com/.well-known/jwks.json'
});
function getKey(header, callback) {
  client.getSigningKey(header.kid, (err, key) => {
    if (err) return callback(err);
    callback(null, key.publicKey || key.rsaPublicKey);
  });
}

An attacker providing jwksUri as http://127.0.0.1:8080/.well-known/jwks.json can make the backend fetch from an internal endpoint, potentially reaching a service that returns sensitive configuration or acts as a pivot to other internal APIs.

In Go, a similar risk appears when using golang.org/x/oauth2 to exchange tokens, and the redirect URL or issuer is taken from user input. If the application uses the raw input to build HTTP requests without URL allowlisting, an attacker can direct the internal HTTP client to sensitive endpoints:

func tokenExchange(w http.ResponseWriter, r *http.Request) {
  issuer := r.FormValue("issuer")
  token := r.FormValue("token")
  conf := &oauth2.Config{
    Endpoint: oauth2.Endpoint{TokenURL: issuer + "/token"},
    ClientID: "my-client",
    ClientSecret: "secret",
  }
  // If issuer is user-controlled and not validated, SSRF can occur
  url := conf.Endpoint.TokenURL + "?grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=" + token
  resp, err := http.Get(url)
  // handle response
}

These JWT-related flows illustrate that SSRF is not only about fetching arbitrary URLs; it becomes critical when those requests interact with internal services that manage or validate JWT material. The risk is elevated when network namespaces allow reaching metadata services, internal APIs, or administrative interfaces that can lead to privilege escalation or token forgery.

Jwt Tokens-Specific Detection

Detecting SSRF in JWT-related code paths requires a scanner that understands both the token handling patterns and the network interactions that occur during validation. middleBrick performs black-box testing by submitting URLs and observing whether outbound requests can be coerced toward internal endpoints. It does not rely on source code but on runtime behavior, which is essential for catching misconfigurations in dynamically built requests.

During a scan, middleBrick runs parallel checks including input validation and unsafe consumption. For JWT flows, it probes endpoints that accept parameters such as jwksUri, issuer, or redirect URLs. It tests with crafted inputs that point to internal IP ranges and reserved addresses (e.g., 169.254.169.254, 127.0.0.1, 10.0.0.1) and inspects whether the server makes a request to those destinations. The scanner correlates findings from the 12 checks to identify whether a JWT validation endpoint is susceptible to SSRF.

To identify the issue manually, review code that builds HTTP requests using values that originate from JWT handling, such as:

  • Parameters that set the JWKS URI for signature verification.
  • Issuer or audience fields that are used to construct discovery URLs.
  • Token introspection endpoints where the token itself is sent and the response may include references to keys or endpoints.

In the dashboard, findings related to SSRF in JWT contexts appear with severity ratings and remediation guidance. The CLI can be used to scan a specific endpoint with a targeted URL, returning JSON output that highlights risky parameters:

middlebrick scan https://api.example.com/oauth/introspect --param jwksUri=http://169.254.169.254/latest/meta-data/iam/security-credentials/

The output includes the affected parameter, the HTTP method, and a description of why the input is considered unsafe. Because SSRF can enable an attacker to learn internal network topology or retrieve service account credentials used to sign JWTs, prioritizing these findings is essential for maintaining the integrity of the token ecosystem.

Jwt Tokens-Specific Remediation

Remediation focuses on strict input validation and avoiding the use of user-controlled data to form network requests that involve JWT material. For JWKS URIs, use a static allowlist of known, verified endpoints rather than accepting a parameter. In Node.js, define a mapping of tenant identifiers to fixed URIs:

const allowedJwks = {
  'tenantA': 'https://auth.example.com/tenantA/.well-known/jwks.json',
  'tenantB': 'https://auth.example.com/tenantB/.well-known/jwks.json',
};
app.get('/jwks', (req, res) => {
  const tenant = req.query.tenant;
  const jwksUri = allowedJwks[tenant];
  if (!jwksUri) return res.status(400).send('Invalid tenant');
  // use jwksUri with jwks-rsa
});

For dynamic client registration or token exchange, validate the issuer against a trusted list and ensure that constructed URLs use a fixed base path. In Go, avoid concatenating user input directly into the token URL. Instead, parse and validate the input, then use a predefined mapping:

validIssuers := map[string]string{
  "https://auth.example.com/tenant1": "tenant1",
  "https://auth.example.com/tenant2": "tenant2",
}
issuer := strings.TrimSuffix(r.FormValue("issuer"), "/")
mapped, ok := validIssuers[issuer]
if !ok {
  http.Error(w, "invalid issuer", http.StatusBadRequest)
  return
}
tokenURL := "https://auth.example.com/token"
// use tokenURL with mapped tenant context

Additionally, enforce network-level restrictions such as egress filtering and ensure that services handling JWTs cannot reach metadata services. Combine these measures with regular scans using middleBrick to verify that no new SSRF paths emerge after changes to JWT validation logic. The Pro plan supports continuous monitoring, which can alert you if a future deployment introduces a parameter that allows constructing requests to internal endpoints, helping maintain a secure JWT handling posture.

Related CWEs: ssrf

CWE IDNameSeverity
CWE-918Server-Side Request Forgery (SSRF) CRITICAL
CWE-441Unintended Proxy or Intermediary (Confused Deputy) HIGH

Frequently Asked Questions

Can SSRF in JWT flows lead to token forgery?
Yes. If an SSRF vulnerability allows an attacker to reach an internal service that manages or signs JWTs, they may obtain signing keys or force the issuance of tokens, leading to token forgery.
How does middleBrick detect SSRF in JWT-related endpoints?
middleBrick tests endpoints that accept parameters influencing network calls during JWT validation, such as JWKS URIs or issuer fields, using internal IP probes and analyzing whether the server makes unintended outbound requests.