Api Key Exposure in Adonisjs with Session Cookies
Api Key Exposure in Adonisjs with Session Cookies — how this specific combination creates or exposes the vulnerability
When an AdonisJS application uses session cookies to carry or manage API keys, the risk of unintended exposure increases because the session store and cookie attributes can inadvertently surface sensitive values. AdonisJS typically relies on signed cookies to preserve integrity, but developers sometimes store or derive API keys in session data, which may be serialized server-side and tied to a cookie identifier. If session cookies are transmitted over unencrypted channels or if the Secure flag is not enforced, an attacker on a network path can intercept the cookie and potentially recover the associated API key.
Another exposure path involves cross-site request forgery (CSRF) or cross-site scripting (XSS) vectors that trick a user’s browser into sending session cookies along with requests that include API key usage. For example, if a route reads an API key from the session to call a third-party service and the response reflects the key or related metadata, an attacker who can force the victim to make requests may learn the key through side channels or response leakage. AdonisJS applications that embed API keys in session state without strict SameSite and HttpOnly settings increase the likelihood of exposure through cookie theft or malicious script execution.
Moreover, improper session invalidation compounds the risk. If an AdonisJS app does not revoke session cookies and associated server-side session data after logout or password changes, an old session cookie might still hold a valid mapping to an active API key. A discovered cookie from a previous session can therefore be reused to access resources or disclose key material if the server’s session handling is inconsistent. This is particularly relevant when middleware that reads the API key from the session does not verify freshness or binding to the current authenticated context, enabling horizontal or vertical privilege escalation through leaked keys.
The interaction with middleware and service providers in AdonisJS also matters. If an application registers a custom provider that reads API keys from the session to configure external clients, and that provider caches or logs values inadvertently, the key may be written to logs or exposed via debug endpoints. Even when the framework signs cookies, inadequate rotation of signing keys can weaken guarantees. An attacker who compromises the signing key might forge session cookies that reference sensitive session data, including API keys, enabling them to masquerade as legitimate users and gain access to protected integrations.
To detect such issues, scans should examine whether API keys appear in session payloads, whether cookies include Secure, HttpOnly, and SameSite attributes, and whether session invalidation is comprehensive. Tools like middleBrick can identify risky patterns in the runtime behavior of AdonisJS apps, such as unauthenticated endpoints that return data conditioned on session-derived keys, and highlight missing cookie protections that facilitate exposure. Remediation centers on removing API keys from session storage, enforcing strict cookie policies, and ensuring that any external calls use securely injected configuration rather than session-derived secrets.
Session Cookies-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on preventing API keys from entering session data and hardening cookie attributes. AdonisJS uses the session package under the hood, and you should configure the session cookie with Secure, HttpOnly, and SameSite settings. Below are concrete code examples that demonstrate safe patterns.
Secure session cookie configuration
Update your session configuration in start/session.ts to enforce strict cookie attributes and avoid storing sensitive values in the session.
import { sessionConfig } from '@ioc:Adonis/Addons/Session'
const sessionConfig: sessionConfig = {
driver: 'cookie',
cookie: {
name: '__Host-session',
path: '/',
httpOnly: true,
secure: true,
sameSite: 'strict',
domain: 'api.example.com',
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
},
adapter: {
crypto: {
secret: process.env.SESSION_CRYPTO_SECRET,
},
},
}
export default sessionConfig
Avoid storing API keys in session
Instead of placing API keys in the session, use environment-based configuration or encrypted vault references. If you must associate a key with a user, store a reference ID in the session and resolve the actual key securely at runtime from a protected source.
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class AuthController {
public async login({ auth, session, response }: HttpContextContract) {
const user = await auth.authenticate()
// Store only a non-sensitive identifier
session.put('userId', user.id)
// Do NOT store API keys in session
// session.put('apiKey', externalService.getKey(user.id))
response.cookie('__Host-session', session.id, {
httpOnly: true,
secure: true,
sameSite: 'strict',
})
return user
}
}
Middleware that safely uses API keys
If an API key must be used for outbound calls, inject it via environment variables or a secure configuration service rather than retrieving it from the session. Below is a middleware example that reads a key from environment configuration, avoiding session exposure.
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import Env from '@ioc:Adonis/Core/Env'
export default class ApiKeyMiddleware {
public async handle(ctx: HttpContextContract, next: () => Promise) {
// Securely fetch from environment, not session
const apiKey = Env.get('EXTERNAL_API_KEY')
if (!apiKey) {
ctx.response.badRequest({ error: 'Server configuration error' })
return
}
// Attach key to request context for downstream use without exposing in session
ctx.extra = { apiKey }
await next()
}
}
Session invalidation on logout
Ensure complete cleanup by destroying the session and clearing the cookie on logout.
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class AuthController {
public async logout({ auth, session, response }: HttpContextContract) {
await auth.use('api').logout()
session.destroy()
response.clearCookie('__Host-session', {
path: '/',
secure: true,
httpOnly: true,
sameSite: 'strict',
})
}
}
Enforce HTTPS in production
Always set secure: true for session cookies in production to prevent transmission over unencrypted channels. In development, you may conditionally disable Secure only on localhost, but never in deployed environments.
const isProduction = Env.get('NODE_ENV') === 'production'
const sessionConfig = {
driver: 'cookie',
cookie: {
secure: isProduction,
httpOnly: true,
sameSite: 'strict',
},
}
export default sessionConfig