Open Redirect in Adonisjs with Mongodb
Open Redirect in Adonisjs with Mongodb — how this specific combination creates or exposes the vulnerability
An open redirect in an AdonisJS application that uses MongoDB for user or configuration data occurs when an endpoint accepts a user-supplied URL or path and performs an HTTP redirect without strict validation. If the target value is derived from or influenced by data stored in MongoDB (for example, a redirect URL saved in a tenant or feature-flag document), the vulnerability is realized at runtime. Attackers can supply a malicious URL that causes the app to read a poisoned document from MongoDB and then redirect the victim.
Because AdonisJS often resolves routes and views dynamically, a common pattern is to read a redirect destination from a request query or body and then call response.redirect(redirectTo). If redirectTo is not validated against a strict allowlist, and the value is sourced from a MongoDB document (e.g., a stored webhook or return URL), the application can be tricked into redirecting authenticated users to attacker-controlled sites. This becomes a phishing aid or a mechanism to bypass referrer-based protections.
In a MongoDB-backed AdonisJS app, the risk is amplified when the stored redirect values are not strictly typed or versioned. For example, a feature-flag collection might include a field like postAuthRedirect. If an attacker can modify that document (via IDOR or a compromised admin account), they can change the redirect target permanently for affected users. Even without data modification, insufficient validation permits arbitrary external URLs, enabling persistent phishing campaigns that appear to originate from your domain.
Mongodb-Specific Remediation in Adonisjs — concrete code fixes
Remediation centers on never trusting data from MongoDB for redirect targets and enforcing strict allowlists. Validate against a per-action or per-client allowlist rather than a global list, and prefer server-side named routes over raw URLs. Below are concrete patterns for AdonisJS with MongoDB documents.
1. Validate against an allowlist derived from route metadata
Define allowed redirect keys in your documents and reject any value not in the keyed set. This avoids parsing or comparing raw URLs stored in MongoDB.
import { schema } from '@ioc:Adonis/Core/Validator'
import Database from '@ioc:Adonis/Lucid/Database'
const redirectSchema = schema.create({
redirectKey: schema.string.optional(),
})
export default class AuthController {
public async redirectAfterLogin({ request, response, auth }) {
const payload = await validate(request, { schema: redirectSchema })
// Define allowed keys; do not use raw URLs from the request
const allowedKeys = new Set(['home', 'dashboard', 'settings'])
const key = payload.redirectKey
let target = '/'
if (key && allowedKeys.has(key)) {
// Safe: key is controlled server-side
target = `/${key}`
}
// If you must read a stored URL, ensure it is from a trusted document and strictly scoped
if (key === 'customReturn') {
const row = await Database.from('redirect_configs').where('key', 'customReturn').first()
if (row && row.allowed_clients?.includes(auth.user?.role || 'guest')) {
// Validate format to prevent open redirect
const url = new URL(row.redirect_url)
if (url.hostname === 'app.yourdomain.com') {
target = url.pathname
}
}
}
return response.redirect(target)
}
}
2. Sanitize stored URLs in MongoDB documents
When storing URLs in MongoDB documents (e.g., tenant settings), enforce a strict schema and normalize hosts. Use the WHATWG URL API to verify that stored URLs resolve to your host before ever using them in a redirect.
import { Url } from 'url'
import Database from '@ioc:Adonis/Lucid/Database'
async function getSafeRedirectUrl(userId: number) {
const doc = await Database.from('user_preferences').where('user_id', userId).first(['redirect_url'])
if (!doc?.redirect_url) return '/'
try {
const parsed = new URL(doc.redirect_url)
// Only allow same-host redirects
if (parsed.hostname !== 'app.yourdomain.com') {
console.warn('Blocked external redirect URL in MongoDB document')
return '/'
}
// Normalize and return path only
return parsed.pathname
} catch (err) {
console.warn('Invalid redirect URL in MongoDB', err)
return '/'
}
}
// Usage in a route handler
export async function authenticatedRedirect({ response, auth }) {
const target = await getSafeRedirectUrl(auth.user!.id)
return response.redirect(target)
}
3. Use server-side named routes instead of raw values
Avoid storing redirect targets in MongoDB entirely where possible. Map keys to named routes defined in your router. This removes the dependency on mutable document values for redirects.
import Route from '@ioc:Adonis/Core/Route'
Route.get('/redirect/:key', async ({ params, response }) => {
const mapping: Record = {
home: '/dashboard',
invoice: '/billing/invoices',
settings: '/settings/profile',
}
const target = mapping[params.key] || '/'
return response.redirect(target)
})