Path Traversal in Adonisjs with Mutual Tls
Path Traversal in Adonisjs with Mutual Tls — how this specific combination creates or exposes the vulnerability
Path Traversal in AdonisJS typically occurs when user-controlled file paths are concatenated or passed to filesystem utilities without proper validation, allowing an attacker to navigate outside the intended directory using sequences like ../. When Mutual TLS (mTLS) is enabled, the server authenticates the client via a client certificate before the application logic runs. While mTLS strengthens transport-layer identity and reduces unauthorized access risk, it does not sanitize file paths. In fact, mTLS can indirectly expose Path Traversal if an authenticated client—such as a service or compromised certificate—is allowed to invoke endpoints that handle file operations.
In AdonisJS, controllers often resolve file paths using constructs like path.join(baseDir, userInput). If userInput is derived from request parameters, headers, or body without canonicalization and strict allowlisting, an authenticated request over mTLS can traverse directories and read sensitive files such as configuration or source code. For example, an authenticated client might send ../../../etc/passwd as a filename parameter; if the application does not validate or restrict the path, the server may return file contents.
During a black-box scan, middleBrick tests unauthenticated attack surfaces and also evaluates endpoints that rely on mTLS for authentication. It checks whether path resolution is properly constrained and whether directory traversal sequences are reflected or leaked in responses. Even with mTLS ensuring that only certificate-bound clients can reach the endpoint, the application must still enforce path safety; otherwise, an authorized client can become an inadvertent vector for data exposure.
Real-world patterns include using fs.readFile or AdonisJS-specific helpers like Drive with unsanitized input. Attackers may also probe for null byte poisoning or OS-specific path separators to bypass naive filters. middleBrick’s checks for Data Exposure and Input Validation highlight such flaws by correlating runtime behavior with OpenAPI specs, ensuring that even mTLS-protected endpoints do not inadvertently expose filesystem traversal.
Mutual Tls-Specific Remediation in Adonisjs — concrete code fixes
To secure AdonisJS applications with mTLS while preventing Path Traversal, combine strict client certificate validation with robust path handling. Use AdonisJS provider-based configuration to enforce mTLS and sanitize all file inputs.
1. Configure Mutual TLS in AdonisJS
Enable mTLS via the AdonisJS HTTPS server configuration. This ensures only clients with trusted certificates can reach your routes.
// start/server.ts
import { defineConfig } from '@adonisjs/core/app'
export default defineConfig({
https: {
enabled: true,
key: '/path/to/server.key',
cert: '/path/to/server.crt',
ca: '/path/to/ca.crt',
requestCert: true,
rejectUnauthorized: true,
},
})
2. Validate and Sanitize File Paths
Never directly concatenate user input into file paths. Use Node.js path utilities and an allowlist of permitted directories.
import { join, resolve, normalize } from 'path'
import { ensureDir, pathExists } from 'fs-extra'
import { ApplicationContract } from '@ioc:Adonis/Core/Application'
export default class FilesController {
public async readFile({ request, response }: HttpContextContract) {
const requested = request.qs().file as string
const baseDir = resolve(__dirname, '..', 'storage', 'public')
// Normalize and resolve path
const normalized = normalize(requested)
const fullPath = resolve(baseDir, normalized)
// Ensure the resolved path is within baseDir
if (!fullPath.startsWith(baseDir)) {
return response.badRequest({ error: 'Invalid file path' })
}
const exists = await pathExists(fullPath)
if (!exists) {
return response.notFound({ error: 'File not found' })
}
// Safely read file
const content = await import('fs/promises').then(({ readFile }) => readFile(fullPath, 'utf-8'))
return response.ok({ content })
}
}
3. Use AdonisJS Drive with Secure Configuration
AdonisJS Drive abstracts filesystem operations. Configure it to restrict to a specific disk and validate inputs.
// config/drive.ts
import { defineDriverConfig } from '@ioc:Adonis/Addons/Drive'
export default {
disks: {
local: {
driver: 'local',
root: resolve(__dirname, '..', 'storage', 'public'),
allowedPaths: ['uploads', 'assets'],
},
},
}
// Example usage in a controller
import { Drive } from '@ioc:Adonis/Addons/Drive'
export class DocumentsController {
public async show({ request, response }: HttpContextContract) {
const fileName = request.qs().name as string
// Drive does not resolve '../' outside allowed paths when configured properly
const fileStream = Drive.getDisk('local').readStream(fileName)
return response.header('Content-Type', 'application/octet-stream').sendStream(fileStream)
}
}
4. Middleware to Enforce Path Safety
Add a route-level middleware to validate paths before they reach controllers, especially when mTLS is in use.
import { Exception } from '@ioc:Adonis/Core/Exceptions'
export const validateFilePath = (allowedPrefix: string[]) => {
return async (ctx: HttpContextContract, next: () => Promise) => {
const requested = ctx.request.qs().file as string
const normalized = normalize(requested)
const isAllowed = allowedPrefix.some(prefix => normalized.startsWith(prefix))
if (!isAllowed) {
throw new Exception('Path traversal attempt blocked', 400, 'E_PATH_TRAVERSAL')
}
await next()
}
}
// In routes.ts
Route.get('/files', 'FilesController.readFile').middleware(['validateFilePath:uploads,assets'])
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |