Clickjacking in Adonisjs with Firestore
Clickjacking in Adonisjs with Firestore — how this specific combination creates or exposes the vulnerability
Clickjacking is a client-side UI redress attack where an attacker tricks a user into clicking or interacting with a hidden or disguised element inside an invisible or overlayer. When an AdonisJS application embeds Firestore-backed content (for example, a dashboard or a form rendered server-side and sent to the browser) without explicit framing defenses, the rendered UI can be embedded inside an attacker-controlled page via an <iframe>. Because AdonisJS does not enforce frame-embedding policies by default, an attacker can overlay transparent UI elements on top of the embedded Firestore-driven content, causing the victim to inadvertently trigger Firestore-bound actions such as document updates or deletions.
In this combination, the risk is amplified when Firestore security rules rely only on authentication and not on context such as the request origin or the referrer. An authenticated user loading an AdonisJS page that embeds a Firestore-driven widget might be redirected to an attacker site that overlays a malicious frame. The attacker can position buttons or links to align with Firestore-triggering UI elements (e.g., a delete or update button rendered by AdonisJS from a Firestore document). Because the request originates from an authenticated session, Firestore rules may permit the action, leading to unauthorized data modification or data exfiltration via social engineering combined with UI manipulation.
AdonisJS typically renders server-side views using Edge or React integrations; if these views include Firestore-bound forms or links, they must be protected with anti-clickjacking mechanisms. Without explicit X-Frame-Options or Content-Security-Policy (frame-ancestors), the browser allows the page to be framed, exposing the Firestore interactions to clickjacking. Even if the Firestore client is initialized securely in AdonisJS, the UI layer remains vulnerable if framing is not restricted at the HTTP response level.
Firestore-Specific Remediation in Adonisjs — concrete code fixes
To mitigate clickjacking in an AdonisJS application that uses Firestore, you should enforce strict frame-embedding controls and ensure that Firestore-bound UI actions are protected by origin checks and anti-CSRF tokens. Below are concrete steps and code examples tailored for AdonisJS with Firestore.
1. Set HTTP headers to prevent framing
Configure AdonisJS to add security headers on every response. This prevents the browser from embedding your app in an attacker’s frame.
// start/hooks.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export const securityHeaders = [
async (ctx: HttpContextContract) => {
ctx.response.headers.set('X-Frame-Options', 'DENY')
ctx.response.headers.set(
'Content-Security-Policy',
"default-src 'self'; frame-ancestors 'none'"
)
},
]
Register the hook in start/kernel.ts to apply it globally:
// start/kernel.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { securityHeaders } from 'App/Hooks/security'
export const globalMiddleware = [
...,
securityHeaders,
]
2. Validate origin for Firestore actions
Even with headers set, validate the request origin for sensitive Firestore operations. In an AdonisJS controller that interacts with Firestore, check the Origin or Referer header before proceeding.
// app/Controllers/Http/FirestoreController.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { Firestore } from '@google-cloud/firestore'
const firestore = new Firestore()
export default class FirestoreController {
public async updateDocument({ request, response }: HttpContextContract) {
const origin = request.headers().origin
const referer = request.headers().referer
const allowedOrigin = 'https://your-adonis-app.com'
if (origin !== allowedOrigin && !referer?.startsWith(allowedOrigin)) {
return response.unauthorized('Invalid request origin')
}
const docId = request.param('id')
await firestore.doc(`items/${docId}`).update({ completed: true })
return response.ok({ status: 'updated' })
}
}
3. Use CSRF protection for Firestore-bound forms
AdonisJS provides built-in CSRF protection for forms. Ensure that any form that triggers Firestore mutations via your backend endpoints includes the CSRF token. For frontend-driven Firestore operations initiated from AdonisJS-rendered pages, include the token in headers.
<!-- resources/views/form.edge -->
<form method="POST" action="/firestore/update/{{ documentId }}">
@csrf
<button type="submit">Update Firestore Document</button>
</form>
For AJAX requests from pages served by AdonisJS, include the CSRF token in the headers:
// resources/js/app.ts
import axios from 'axios'
axios.post('/firestore/update/123', { data: 'value' }, {
headers: {
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '',
},
})
4. Avoid embedding sensitive Firestore pages in iframes
If your Firestore-driven views must be embeddable, explicitly allow framing only from trusted origins using CSP frame-ancestors. Do not use ALLOW-FROM as it is deprecated.
// Example: allow framing only by your admin domain
ctx.response.headers.set(
'Content-Security-Policy',
"default-src 'self'; frame-ancestors 'self' https://trusted-admin.example.com"
)