Token Leakage in Adonisjs with Dynamodb
Token Leakage in Adonisjs with Dynamodb — how this specific combination creates or exposes the vulnerability
Token leakage in an AdonisJS application that uses DynamoDB typically occurs when session or authentication tokens are stored, transmitted, or logged in a way that exposes them to unauthorized parties. Because AdonisJS does not enforce a specific session store, developers sometimes choose DynamoDB as a scalable backend for storing session data. If the application writes raw tokens into DynamoDB items without proper protection, an attacker who gains read access to the table—through misconfigured IAM policies, accidental exposure via backups, or insufficient VPC/network controls—can harvest valid tokens.
DynamoDB-specific risks amplify this problem when item attributes are not carefully constrained. For example, storing a JWT in a "token" attribute without encryption means anyone who can perform a GetItem or Scan on the table can read the token. Additionally, logging mechanisms in AdonisJS that include request payloads or session identifiers might inadvertently write tokens to CloudWatch or application logs when debugging is enabled. A common anti-pattern is using the primary key in a predictable way (e.g., userId as the partition key) without additional randomization, making it easier for an attacker to iterate over known user IDs and attempt GetItem calls to discover valid tokens.
Another vector specific to AdonisJS involves misconfigured HTTP-only and Secure flags. If tokens are stored in cookies without the Secure and HttpOnly flags set, client-side JavaScript may read them, enabling cross-site scripting (XSS) to exfiltrate tokens. When combined with DynamoDB, an attacker who steals a token can use it to impersonate a user indefinitely until the token is revoked or expires. The framework’s session management utilities must explicitly set secure cookie attributes and avoid placing sensitive material in query strings or response bodies that might be captured by logs or browser history.
To detect these issues, middleBrick scans the unauthenticated attack surface of an AdonisJS endpoint that uses DynamoDB, checking whether tokens appear in logs, whether DynamoDB item attributes expose sensitive values, and whether insecure cookie attributes are present. The scan examines OpenAPI specifications if available, cross-referencing defined responses with runtime behavior to identify mismatches that could lead to token exposure. This is critical because AdonisJS applications often expose RESTful routes that return session identifiers or tokens in JSON payloads when they should return opaque references only.
Because DynamoDB retains data until explicitly deleted, any token written to the table remains recoverable even after the client deletes its cookie. This persistence increases the impact of token leakage, especially when items are replicated or backed up. Developers must ensure that token storage is short-lived, encrypted at rest, and tightly scoped with IAM policies that limit who can read or write session items. middleBrick’s checks include verifying that tokens are not present in logs or error messages returned by AdonisJS routes and that DynamoDB table policies do not allow overly broad read access.
Remediation guidance centers on encrypting tokens before they touch DynamoDB, using short-lived sessions, and enforcing strict cookie attributes within AdonisJS middleware. The framework provides hooks to modify session data before it is persisted, allowing developers to store only opaque session identifiers while keeping tokens in memory or secure HTTP-only cookies. By combining runtime scanning with secure coding practices, teams can reduce the likelihood of token leakage in AdonisJS applications backed by DynamoDB.
Dynamodb-Specific Remediation in Adonisjs — concrete code fixes
To remediate token leakage when using DynamoDB with AdonisJS, start by ensuring tokens are never stored in plain text in DynamoDB items. Instead of persisting raw JWTs, store a salted hash or a random opaque session identifier, and keep the actual token in memory or in a secure, HTTP-only cookie. Below are concrete code examples that demonstrate secure session handling and DynamoDB interactions within AdonisJS.
First, configure session settings in start/session.js to enforce secure cookies:
const { defineConfig } = require('@ioc:Adonis/Addons/Session')
module.exports = defineConfig({
driver: 'dynamodb',
useSameSiteCookie: true,
cookie: {
httpOnly: true,
secure: true,
sameSite: 'strict',
path: '/',
},
dynamodb: {
tableName: 'app_sessions',
partitionKey: 'sessionId',
region: 'us-east-1',
},
})
This configuration ensures that session cookies are not accessible to JavaScript and are only sent over HTTPS. The DynamoDB driver for AdonisJS session management should use a dedicated table with a partition key of sessionId, avoiding predictable keys based on user identifiers.
Next, implement secure token storage by writing only opaque identifiers to DynamoDB. In your authentication controller, generate a random session ID and store metadata separately without the raw token:
const crypto = require('node:crypto')
const Session = use('App/Models/Session')
async function createSecureSession({ request, response }) {
const { email, password } = request.all()
const user = await User.findBy('email', email)
if (!user || !(await Hash.verify(password, user.password))) {
return response.status(401).send({ error: 'Invalid credentials' })
}
const token = crypto.randomBytes(64).toString('hex') // opaque token
const sessionId = crypto.randomUUID()
await Session.create({
sessionId,
userId: user.id,
tokenHash: hashToken(token), // e.g., SHA-256 with salt
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
})
response.cookie('session_id', sessionId, {
httpOnly: true,
secure: true,
sameSite: 'strict',
path: '/',
}).send({ ok: true })
}
function hashToken(token) {
const salt = 'static-salt-placeholder-use-env-vars'
return crypto.createHmac('sha256', salt).update(token).digest('hex')
}
When validating a session, retrieve the item from DynamoDB using the opaque sessionId and compare the hash:
const Session = use('App/Models/Session')
async function validateSession({ request }) {
const sessionId = request.cookie('session_id')
if (!sessionId) return null
const session = await Session.findBy('sessionId', sessionId)
if (!session || new Date() > session.expiresAt) return null
// token validation logic, e.g., compare hash from request header
return session
}
Finally, ensure your DynamoDB table policies restrict access to session items. Use IAM roles that grant least privilege and enable encryption at rest. middleBrick can verify these configurations by scanning your endpoints and checking whether tokens appear in responses or logs, and whether DynamoDB permissions are appropriately scoped.