Clickjacking in Adonisjs
How Clickjacking Manifests in Adonisjs
Clickjacking in Adonisjs applications typically exploits the framework's view rendering system and HTTP response handling. The vulnerability arises when an Adonisjs application fails to implement proper X-Frame-Options headers or Content Security Policy (CSP) directives, allowing malicious websites to embed your application's pages within iframes.
In Adonisjs, this often occurs in controller actions that render sensitive pages without frame protection. For example, an admin dashboard route like:
Route.get('/admin/dashboard', 'AdminController.dashboard')If the dashboard view contains forms for critical operations (user management, payment processing, data export), an attacker can create a malicious page that loads this dashboard in an invisible iframe. The attacker then tricks users into clicking on seemingly innocuous buttons that actually trigger actions in the hidden iframe.
Adonisjs's view engine (Edge) can inadvertently contribute to clickjacking risks when developers use partial views for sensitive components without considering frame protection. A common pattern is embedding authentication forms or confirmation dialogs as reusable components:
@component('components.auth-modal') @endcomponentWithout proper headers, these components become vulnerable when rendered in contexts where they shouldn't be directly accessible.
The framework's middleware system provides opportunities for clickjacking protection, but developers often overlook adding frame-busting headers. Adonisjs's built-in middleware doesn't include clickjacking protection by default, requiring explicit implementation in either global middleware or specific route groups.
Adonisjs-Specific Detection
Detecting clickjacking vulnerabilities in Adonisjs applications requires examining both the HTTP response headers and the application's routing structure. middleBrick's black-box scanning approach is particularly effective here, as it tests the actual runtime behavior without requiring source code access.
When middleBrick scans an Adonisjs endpoint, it specifically checks for:
- Missing X-Frame-Options header (DENY or SAMEORIGIN values)
- Missing Content-Security-Policy frame-ancestors directive
- Missing frame-busting JavaScript patterns
- Exposed sensitive endpoints that should never be framed
For Adonisjs applications, middleBrick's OpenAPI analysis is especially valuable. It examines your API specifications to identify endpoints that handle sensitive operations, then cross-references these with actual HTTP responses to verify frame protection is in place.
Developers can also perform manual detection using curl or browser developer tools:
curl -I https://yourapp.com/admin/dashboardLook for X-Frame-Options and Content-Security-Policy headers. In Adonisjs, you can create a diagnostic middleware to log frame-related headers:
class FrameSecurityMiddleware {
async handle({ response }, next) {
await next()
console.log('X-Frame-Options:', response.headers['x-frame-options'])
console.log('Content-Security-Policy:', response.headers['content-security-policy'])
}}Adonisjs-Specific Remediation
Adonisjs provides several native approaches to prevent clickjacking. The most straightforward is implementing a middleware that sets appropriate headers. Here's an Adonisjs-specific implementation:
class FrameSecurityMiddleware {
async handle({ response }, next) {
response.header('X-Frame-Options', 'DENY')
response.header('Content-Security-Policy', "frame-ancestors 'none'")
await next()
}
}Register this middleware globally in start/kernel.ts or apply it selectively to sensitive route groups:
Route.group(() => {
Route.get('/admin/dashboard', 'AdminController.dashboard')
Route.post('/admin/users', 'AdminController.createUser')
}).middleware(['frame-security'])For applications requiring more granular control, Adonisjs's middleware can inspect the request context:
class FrameSecurityMiddleware {
async handle({ response, request }, next) {
const isSensitiveRoute = request.url().includes('/admin')
if (isSensitiveRoute) {
response.header('X-Frame-Options', 'DENY')
response.header('Content-Security-Policy', "frame-ancestors 'none'")
}
await next()
}
}Adonisjs's view system also allows frame-busting JavaScript as a fallback. Include this in your main layout or specific sensitive views:
@if(Config.get('app.env') === 'production')
<script>
if (top.location != location) {
top.location.href = location.href
}
</script>
@endifFor comprehensive protection, combine header-based and JavaScript-based approaches. Adonisjs's middleware system makes this straightforward:
class FrameSecurityMiddleware {
async handle({ response }, next) {
response.header('X-Frame-Options', 'DENY')
response.header('Content-Security-Policy', "frame-ancestors 'none'")
response.implicitEnd = false
await next()
// Add frame-busting script if headers might be stripped
response.send(response.response.toString() + '
<script>if(top!=self)top.location=self.location;</script>')
}
}