Zip Slip in Adonisjs with Jwt Tokens
Zip Slip in Adonisjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability
Zip Slip is a path traversal vulnerability where an attacker-supplied archive or file path traverses outside the intended extraction directory, often via malicious zip files. In AdonisJS applications that handle file uploads and also use JWT Tokens for authentication and authorization, the combination can expose a critical security gap if file operations are tied to identity claims without strict path validation.
Consider an AdonisJS route that accepts an uploaded archive and extracts it to a user-specific directory derived from a JWT Token payload. If the JWT Token identifies the user but the server uses unsanitized input from the archive entries to construct filesystem paths, an attacker can include paths like ../../../etc/passwd or ../../../../var/www/html/.env inside the archive. When the archive is extracted, these paths can overwrite arbitrary files or escape the intended directory, regardless of the JWT Token’s correctness.
This risk is amplified when JWT Tokens carry user identifiers used to derive storage paths without canonicalization. For example, an endpoint that trusts the sub claim to build a destination folder (uploads/${userId}/files) may still process malicious filenames if extraction logic does not validate each entry. The JWT Token ensures the request comes from an authenticated user, but it does not guarantee that the user’s actions are confined to their own directory if path traversal is possible.
In AdonisJS, this can occur in controller logic that uses third-party unzip libraries without sanitizing entry names. Even with JWT-based authentication middleware in place, if the server does not normalize and restrict extraction paths, the attack surface remains open. Real-world patterns include using adm-zip or similar packages where entries are iterated and extracted naively.
Because middleBrick scans unauthenticated attack surfaces, it can detect such path traversal risks even when JWT Tokens are required for access, by testing endpoint behavior with traversal patterns. Findings will highlight missing path canonicalization and provide remediation guidance mapped to frameworks like OWASP API Top 10 and relevant compliance controls.
Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on strict path validation, canonicalization, and avoiding any direct use of user-controlled filenames from archives. Below are concrete code examples for secure handling in AdonisJS when JWT Tokens are used for authentication.
1. Validate and sanitize archive entries before extraction
import { join, resolve, normalize } from 'path'
import { existsSync, mkdirSync } from 'fs'
import { ApiResponse } from '@ioc:Adonis/Core/Helpers'
export default class ArchivesController {
public async extractUserArchive({ request, auth }) {
const user = auth.user! // JWT Token validated user
const userId = user.id
const baseDir = resolve(`uploads/${userId}`)
// Ensure base directory exists
if (!existsSync(baseDir)) {
mkdirSync(baseDir, { recursive: true })
}
const archiveBuffer = request.file('archive')?.buffer
if (!archiveBuffer) {
return ApiResponse.badRequest({ message: 'Archive file is required' })
}
const zip = new (require('adm-zip'))(archiveBuffer)
const zipEntries = zip.getEntries()
for (const entry of zipEntries) {
// Normalize and resolve the target path
const targetPath = resolve(baseDir, entry.entryName)
// Ensure the normalized path starts with the base directory
if (!targetPath.startsWith(baseDir)) {
throw new Error('Invalid archive entry: path traversal detected')
}
// Reject entries containing null bytes or suspicious patterns
if (entry.entryName.includes('\0') || entry.entryName.includes('..')) {
throw new Error('Invalid archive entry: unsafe filename')
}
if (entry.isDirectory) {
mkdirSync(targetPath, { recursive: true })
} else {
// Ensure parent directories exist
const parentDir = resolve(targetPath, '..')
if (!existsSync(parentDir)) {
mkdirSync(parentDir, { recursive: true })
}
entry.extract({ path: parentDir, maintainEntryDirectory: false })
}
}
return ApiResponse.ok({ message: 'Archive extracted successfully' })
}
}
2. Use a dedicated extraction utility with strict options
Instead of manual loops, prefer libraries that support safe extraction modes. For example, using unzipper with strict path checks:
import { response } from 'celebrate'
import { createReadStream, createWriteStream, mkdirSync } from 'fs'
import { resolve } from 'path'
import { pipeline } from 'stream'
import { promisify } from 'util'
import { ApiResponse } from '@ioc:Adonis/Core/Helpers'
const pump = promisify(pipeline)
export default class SafeArchivesController {
public async extractSafe({ request, auth }) {
const user = auth.user!
const userId = user.id
const baseDir = resolve(`uploads/${userId}`)
mkdirSync(baseDir, { recursive: true })
const archiveStream = request.file('archive')?.createReadStream()
if (!archiveStream) {
return ApiResponse.badRequest({ message: 'Archive file is required' })
}
// Use unzipper with strict options
const unzipper = require('unzipper')
const parser = unzipper.Parse()
archiveStream.pipe(parser)
for await (const entry of parser) {
const fileName = entry.path
const type = entry.type
// Validate entry path
if (fileName.includes('..') || fileName.includes('\0')) {
entry.autodrain()
throw new Error('Invalid archive entry: path traversal detected')
}
const destination = resolve(baseDir, fileName)
if (!destination.startsWith(baseDir)) {
entry.autodrain()
throw new Error('Invalid archive entry: extraction outside base directory')
}
if (type === 'Directory') {
mkdirSync(destination, { recursive: true })
entry.autodrain()
} else {
await pump(entry, createWriteStream(destination))
}
}
return ApiResponse.ok({ message: 'Archive extracted safely' })
}
}
3. JWT Token usage remains unchanged but must not influence path resolution
Ensure JWT authentication middleware validates tokens, but do not derive filesystem paths directly from claims that could be manipulated server-side. Use the user ID for namespacing only after strict validation.
These fixes ensure that even when JWT Tokens authenticate requests, file extraction cannot escape intended boundaries. middleBrick can verify such protections by scanning endpoints that handle archives and JWT-based auth, highlighting missing canonicalization and unsafe extraction patterns.