CWE-22 in Restify
How Cwe 22 Manifests in Restify
CWE-22 (Path Traversal) occurs when an application uses attacker-controlled input to construct file paths without proper validation. In Restify applications, this vulnerability commonly appears in several specific patterns:
const restify = require('restify');
const fs = require('fs');
const server = restify.createServer();
// Vulnerable: direct path concatenation
server.get('/download/:filename', (req, res) => {
const filePath = `/uploads/${req.params.filename}`;
fs.readFile(filePath, (err, data) => {
if (err) return res.send(404);
res.send(data);
});
});
// Vulnerable: path.join without normalization
server.get('/static/:file', (req, res) => {
const filePath = path.join(__dirname, 'public', req.params.file);
fs.readFile(filePath, (err, data) => {
if (err) return res.send(500);
res.send(data);
});
});
// Vulnerable: user-controlled directory traversal
server.get('/user-files/:user/:file', (req, res) => {
const filePath = path.join(__dirname, 'files', req.params.user, req.params.file);
fs.readFile(filePath, (err, data) => {
if (err) return res.send(404);
res.send(data);
});
});Attackers can exploit these patterns by sending requests like:
GET /download/../../etc/passwd
GET /static/../../package.json
GET /user-files/../../../../etc/passwdThe core issue is that Restify's default routing doesn't sanitize path parameters. When combined with Node.js's path.join() or string concatenation, attackers can navigate outside intended directories using ../ sequences.
Restify's middleware architecture makes this particularly dangerous because path parameters are accessible throughout the request lifecycle. Even if you validate later in your handler, the path has already been constructed insecurely.
Another Restify-specific manifestation occurs with file uploads and static serving:
const serveStatic = require('serve-static');
server.get('/static/:path', serveStatic(__dirname + '/public'));
// Vulnerable: dynamic file serving without validation
server.get('/upload/:user/:filename', (req, res) => {
const userDir = path.join(__dirname, 'uploads', req.params.user);
const filePath = path.join(userDir, req.params.filename);
fs.createReadStream(filePath).pipe(res);
});The last example is especially problematic because it allows directory traversal across user boundaries, potentially exposing other users' files.
Restify-Specific Detection
Detecting CWE-22 in Restify applications requires both static analysis and runtime scanning. Here's how to identify path traversal vulnerabilities:
Static Code Analysis
// Search for dangerous patterns in your Restify codebase
const fs = require('fs');
const path = require('path');
function scanForPathTraversal(filePath) {
const code = fs.readFileSync(filePath, 'utf8');
const patterns = [
/path\.join\(.*req\./g, // path.join with request params
/\+\s*req\./g, // string concatenation with request params
/fs\.readFile\(.*req\./g, // fs.readFile with request params
/fs\.createReadStream\(.*req\./g // fs.createReadStream with request params
];
const findings = [];
patterns.forEach(pattern => {
const matches = code.match(pattern);
if (matches) {
findings.push({
pattern: pattern.toString(),
count: matches.length,
file: filePath
});
}
});
return findings;
}Runtime Scanning with middleBrick
middleBrick provides automated CWE-22 detection for Restify APIs through black-box scanning. Simply provide your API URL:
# Install middleBrick CLI
npm install -g middlebrick
# Scan your Restify API endpoint
middlebrick scan https://api.yourservice.com
# Or integrate into your CI/CD pipeline
middlebrick scan --url https://api.yourservice.com --fail-below BmiddleBrick tests for path traversal by attempting to access files outside your intended directories using common traversal sequences:
../etc/passwd
../../etc/passwd
..\..\..\windows\system32\drivers\etc\hosts
/.//././etc/passwdThe scanner reports findings with severity levels and specific remediation guidance. For Restify applications, middleBrick identifies:
- Unvalidated path parameters in route definitions
- Unsafe file system operations using request data
- Missing input validation for file paths
- Directory traversal attempts that succeed
Manual Testing
Test your Restify endpoints manually by attempting to access known system files:
# Test path traversal attempts
curl -v https://api.yourservice.com/download/../../etc/passwd
curl -v https://api.yourservice.com/static/../../../../etc/passwd
Look for HTTP 200 responses with file contents, which indicate successful traversal.
Restify-Specific Remediation
Securing Restify against CWE-22 requires a defense-in-depth approach. Here are Restify-specific remediation strategies:
1. Input Validation with Path Normalization
const restify = require('restify');
const path = require('path');
const fs = require('fs');
const server = restify.createServer();
// Secure file serving with validation
function safeReadFile(baseDir, userPath) {
const normalized = path.normalize(userPath);
// Verify the resolved path is within the base directory
if (!resolved.startsWith(baseDir)) {
throw new Error('Path traversal attempt detected');
}
return resolved;
}
server.get('/download/:filename', (req, res) => {
try {
const safePath = safeReadFile(__dirname + '/uploads', req.params.filename);
fs.readFile(safePath, (err, data) => {
if (err) return res.send(404);
res.send(data);
});
} catch (error) {
res.send(400, { error: 'Invalid file path' });
}
});2. Whitelist-Based File Access
const allowedFiles = new Set(['document.pdf', 'report.xlsx', 'photo.jpg']);
server.get('/download/:filename', (req, res) => {
const filename = req.params.filename;
if (!allowedFiles.has(filename)) {
return res.send(400, { error: 'File not allowed' });
}
const filePath = path.join(__dirname, 'uploads', filename);
fs.readFile(filePath, (err, data) => {
if (err) return res.send(404);
res.send(data);
});
});3. Restify Middleware for Path Validation
function pathTraversalProtection(req, res, next) {
const dangerousPatterns = [
///////, // multiple slashes
/////////, // excessive slashes
/\.\./, // parent directory
/\x00/ // null byte injection
];
const { filename } = req.params;
if (filename && dangerousPatterns.some(pattern => pattern.test(filename))) {
return res.send(400, { error: 'Invalid file path' });
}
next();
}
server.get('/download/:filename', pathTraversalProtection, (req, res) => {
const filePath = path.join(__dirname, 'uploads', req.params.filename);
fs.readFile(filePath, (err, data) => {
if (err) return res.send(404);
res.send(data);
});
});4. Using Restify's Built-in Security Features
const restify = require('restify');
const server = restify.createServer();
// Add security middleware
server.use(restify.plugins.conditionalRequest());
server.use(restify.plugins.acceptParser(server.acceptable));
// Custom security middleware
function securityHeaders(req, res, next) {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
next();
}
server.use(securityHeaders);
// Secure static file serving
const serveStatic = require('serve-static');
const safeStatic = serveStatic(__dirname + '/public', {
fallthrough: false,
setHeaders: (res, path) => {
if (!path.startsWith(__dirname + '/public')) {
res.statusCode = 403;
res.end();
}
}
});
server.get('/static/:path', (req, res, next) => {
safeStatic(req, res, next);
});5. Regular Security Scanning
Integrate middleBrick into your development workflow to continuously scan for CWE-22:
# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Scan for Path Traversal
run: |
npm install -g middlebrick
middlebrick scan https://staging.yourservice.com --fail-below B
continue-on-error: true6. Runtime Protection
function validateFilePath(baseDir, userPath) {
// Normalize and resolve the path
const normalized = path.normalize(userPath);
const resolved = path.resolve(baseDir, normalized);
// Check if resolved path is within base directory
if (!resolved.startsWith(baseDir)) {
return { valid: false, error: 'Path traversal detected' };
}
// Check if file exists and is accessible
try {
const stats = fs.statSync(resolved);
if (!stats.isFile()) {
return { valid: false, error: 'Not a file' };
}
return { valid: true, path: resolved };
} catch (err) {
return { valid: false, error: 'File not found' };
}
}By implementing these Restify-specific protections, you create multiple layers of defense against path traversal attacks, significantly reducing your API's attack surface.