Dns Rebinding in Adonisjs
How Dns Rebinding Manifests in Adonisjs
Dns Rebinding in Adonisjs applications typically exploits the framework's default CORS configuration and HTTP client behavior. When Adonisjs serves APIs with permissive CORS settings, an attacker can register a malicious domain that resolves to their own IP, then rapidly switch DNS records to point to the victim's internal network.
The classic Adonisjs vulnerability pattern involves an API endpoint that accepts URLs as parameters and makes internal HTTP requests. Consider this common pattern in Adonisjs controllers:
class ExternalDataService {
async fetchData({ request }) {
const url = request.input('endpoint');
const response = await axios.get(url); // Vulnerable to DNS rebinding
return response.data;
}
}The issue compounds when combined with Adonisjs's default CORS middleware. Many Adonisjs applications enable:
// config/cors.js
module.exports = {
origin: true, // Allows any origin
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true
}This permissive configuration, paired with URL-based request handling, creates an attack vector. An attacker's malicious site can trigger requests that appear to originate from the same origin due to DNS rebinding, bypassing CORS protections.
Adonisjs's HTTP client (axios) doesn't implement DNS pinning by default, making it susceptible to rebinding attacks. When processing a URL parameter, the client resolves the hostname at request time, allowing the DNS switch to redirect traffic to internal IPs like 192.168.1.1 or 10.0.0.1.
Another Adonisjs-specific manifestation occurs with WebSocket connections. Adonisjs's WebSocket server, when configured without origin verification, can be targeted by rebinding attacks that hijack authenticated sessions:
const ws = require('@adonisjs/websocket-server')
const server = ws.Server;
server.listen(3333); // No origin verification by defaultThe framework's reliance on environment variables for configuration can also introduce rebinding risks if internal services are referenced by hostname rather than IP, allowing DNS manipulation to redirect traffic.
Adonisjs-Specific Detection
Detecting DNS rebinding in Adonisjs requires examining both configuration files and runtime behavior. Start by auditing your config/cors.js file for overly permissive settings:
// Dangerous configuration
module.exports = {
origin: true, // Any origin allowed
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
credentials: true,
allowedHeaders: ['*']
}Use middleBrick's API security scanner to identify these vulnerabilities automatically. The scanner tests your Adonisjs endpoints against DNS rebinding scenarios without requiring credentials:
npx middlebrick scan https://your-adonis-app.com/api/data
middleBrick specifically checks for:
- Permissive CORS configurations that allow DNS rebinding bypasses
- Endpoints accepting URL parameters that could be exploited
- Internal network access attempts from external origins
- WebSocket endpoints without proper origin verification
- Configuration files exposing internal service references
For code-level detection, implement middleware that validates request origins against a whitelist:
class OriginValidator {
async handle({ request, response }, next) {
const allowedOrigins = ['https://yourdomain.com'];
const origin = request.header('origin');
if (!allowedOrigins.includes(origin)) {
return response.status(403).send('Forbidden origin');
}
await next();
}
}
// Register in start/kernel.js
const globalMiddleware = [
'App/Middleware/OriginValidator'
]Monitor your Adonisjs application logs for suspicious patterns: multiple rapid requests to the same hostname that resolve to different IPs, or requests to non-routable IP ranges from external origins.
Adonisjs-Specific Remediation
Remediating DNS rebinding in Adonisjs requires a multi-layered approach. First, secure your CORS configuration by explicitly defining allowed origins:
// config/cors.js
module.exports = {
origin: ['https://yourdomain.com', 'https://yourapp.com'],
methods: ['GET', 'POST'],
credentials: false,
allowedHeaders: ['Content-Type', 'Authorization']
}For URL parameter handling, implement strict validation and use IP-based restrictions:
const { validate } = use('Validator');
const { parse } = use('url');
class ExternalDataService {
async fetchData({ request }) {
const rules = {
endpoint: 'required|url',
allowed_domains: ['yourtrusteddomain.com']
};
const validation = await validate(request.all(), rules);
if (validation.fails()) {
return response.status(400).send('Invalid URL');
}
const url = request.input('endpoint');
const parsed = parse(url);
// Block internal network access
const privateIps = /^(192\.168|10\.|172\.(1[6-9]|2[0-9]|3[0-1]))/;
if (privateIps.test(parsed.hostname)) {
return response.status(403).send('Internal network access forbidden');
}
// Only allow whitelisted domains
if (!this.isWhitelisted(parsed.hostname)) {
return response.status(403).send('Domain not allowed');
}
const response = await axios.get(url, {
timeout: 5000,
maxRedirects: 0
});
return response.data;
}
isWhitelisted(hostname) {
const whitelist = ['api.yourtrusteddomain.com', 'yourotherdomain.com'];
return whitelist.includes(hostname);
}
}Implement DNS pinning at the application level to prevent resolution changes during a request:
const dns = require('dns');
const memoizedLookups = new Map();
function dnsLookup(hostname) {
if (memoizedLookups.has(hostname)) {
return memoizedLookups.get(hostname);
}
return new Promise((resolve, reject) => {
dns.lookup(hostname, (err, address) => {
if (err) return reject(err);
memoizedLookups.set(hostname, address);
resolve(address);
});
});
}
// Use in your service
const ip = await dnsLookup(parsed.hostname);
if (this.isPrivateIP(ip)) {
throw new Error('Private IP blocked');
}For WebSocket security, implement origin verification middleware:
const ws = require('@adonisjs/websocket-server');
const server = ws.Server;
server.on('connection', (socket) => {
const origin = socket.request.headers.origin;
const allowedOrigins = ['https://yourdomain.com'];
if (!allowedOrigins.includes(origin)) {
socket.terminate();
return;
}
});
server.listen(3333);Finally, integrate middleBrick into your CI/CD pipeline to continuously scan for DNS rebinding vulnerabilities:
# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run middleBrick Scan
run: npx middlebrick scan https://staging-yourapp.com
continue-on-error: true
- name: Fail on high risk
if: failure()
run: exit 1