Server Side Template Injection with Jwt Tokens
How Server Side Template Injection Manifests in Jwt Tokens
Server Side Template Injection (SSTI) in JWT tokens occurs when an attacker can manipulate token claims to inject template expressions that get evaluated server-side. Unlike traditional SSTI where attackers inject code into HTML templates, JWT SSTI exploits the way certain libraries process token claims without proper sanitization.
The most common JWT SSTI vector targets the kid (key ID) header claim. When a JWT library dynamically loads keys based on the kid value, an attacker can craft a JWT where the kid contains template expressions that get evaluated. For example:
// Vulnerable code pattern
const token = jwt.sign(
{ userId: 1 },
{
key: fs.readFileSync(`keys/${kid}.pem`), // SSTI here if kid is untrusted
header: { kid: req.headers['kid'] }
}
);If an attacker sets the kid header to something like ../../../etc/passwd or template expressions, they can achieve directory traversal or command execution depending on how the key loading is implemented.
Another JWT-specific SSTI pattern involves claim-based template injection in custom claim processing. Some applications store template expressions in custom claims like data or metadata that get processed without validation:
// Malicious JWT payload
{
"sub": "user123",
"data": "{{config.__class__.__init__.__globals__['os'].popen('id').read()}}"
}When this token is processed by a vulnerable application using a template engine that evaluates expressions, it can lead to arbitrary code execution. This is particularly dangerous in applications that use JWTs for both authentication and configuration storage.
LLM-based JWT processing introduces new SSTI vectors. When AI models process JWT claims for authorization decisions, they can be vulnerable to prompt injection through carefully crafted token claims. An attacker might embed system prompt manipulation in JWT claims that gets processed by an LLM-based authorization system.
JWT-Specific Detection
Detecting SSTI in JWT tokens requires examining both the token structure and the processing logic. middleBrick's JWT security scanner specifically targets these vulnerabilities through multiple detection methods.
The scanner first analyzes JWT token claims for suspicious patterns. It looks for claims containing template syntax, unusual characters, or patterns that suggest injection attempts. For the kid header specifically, middleBrick checks if the value contains path traversal sequences or template expressions.
middleBrick also examines how JWT libraries are configured in the target application. It identifies vulnerable patterns like dynamic key loading based on untrusted kid values, use of unsafe template engines in claim processing, and improper validation of custom claims. The scanner tests common SSTI payloads against the JWT processing endpoints.
For LLM-based JWT processing, middleBrick includes specialized AI security checks. It tests for system prompt leakage by attempting to extract template expressions from JWT claims, and it probes for prompt injection vulnerabilities by crafting tokens with adversarial payloads designed to manipulate AI-based authorization logic.
The detection process includes:
- Claim content analysis for template syntax and suspicious patterns
- Header claim validation, especially for
kidand custom headers - Library version checking against known SSTI vulnerabilities
- Dynamic key loading behavior analysis
- LLM processing endpoint testing for AI-specific SSTI vectors
middleBrick provides a security risk score with detailed findings, showing exactly which JWT claims or processing patterns are vulnerable to SSTI attacks and what the potential impact could be.
JWT-Specific Remediation
Remediating JWT SSTI requires both input validation and safe processing patterns. The most critical fix is implementing strict validation of all JWT claims, especially header claims like kid.
For the kid header specifically, use a whitelist approach:
const VALID_KID_VALUES = ['key1', 'key2', 'key3']; // Predefined allowed values
function validateKid(kid) {
if (!VALID_KID_VALUES.includes(kid)) {
throw new Error('Invalid key ID');
}
return kid;
}
// Usage
const token = jwt.sign(
payload,
{
key: fs.readFileSync(`keys/${validateKid(req.headers.kid)}.pem`),
header: { kid: validateKid(req.headers.kid) }
}
);This prevents attackers from injecting arbitrary values into the kid header.
For custom claims that might contain user data, implement strict sanitization and avoid using template engines on untrusted input:
function sanitizeClaim(claim) {
// Remove any template syntax
const templatePattern = /{{.*?}}/g;
if (templatePattern.test(claim)) {
throw new Error('Template syntax not allowed in claims');
}
return claim;
}
// Validate all claims before processing
const decoded = jwt.verify(token, secret);
Object.keys(decoded).forEach(key => {
sanitizeClaim(decoded[key]);
});When using JWTs for configuration or metadata storage, implement a schema validation approach:
const Joi = require('joi');
const claimSchema = Joi.object({
sub: Joi.string().required(),
data: Joi.string().optional(),
metadata: Joi.object({
safeField: Joi.string().optional()
}).optional()
});
// Validate token claims
const { error } = claimSchema.validate(decoded);
if (error) {
throw new Error('Invalid token claims structure');
}For LLM-based JWT processing, implement input filtering and context isolation:
function filterLLMJWTClaims(claims) {
// Remove or sanitize claims that could affect LLM context
const filtered = { ...claims };
delete filtered.system;
delete filtered.prompt;
delete filtered['x-jwt-injection'];
// Sanitize remaining claims
Object.keys(filtered).forEach(key => {
filtered[key] = String(filtered[key]).replace(/[{}]/g, '');
});
return filtered;
}
// Use filtered claims with LLM
const safeClaims = filterLLMJWTClaims(decoded);
const response = await llm.generate(safeClaims);Finally, keep JWT libraries updated and use well-vetted implementations that have built-in protections against SSTI patterns. middleBrick's continuous monitoring can alert you if new vulnerabilities are discovered in your JWT processing implementation.