HIGH clickjackingadonisjsbasic auth

Clickjacking in Adonisjs with Basic Auth

Clickjacking in Adonisjs with Basic Auth — how this specific combination creates or exposes the vulnerability

Clickjacking is a client-side UI redressing attack where an attacker tricks a user into interacting with a hidden or disguised element inside an invisible or disguised iframe. When Basic Authentication is used in an AdonisJS application, the browser may automatically send credentials with requests to the application’s endpoints. If the application does not set appropriate anti-clickjacking headers, an attacker can embed the application’s URLs in an iframe and lure authenticated users to interact with controls they do not see, potentially triggering privileged actions protected by Basic Auth.

Basic Auth in AdonisJS is commonly implemented via an authentication middleware that checks the Authorization header. Because the browser handles credentials automatically once the user has entered them, an authenticated session can be exploited in a clickjacking scenario if the application’s responses are rendered inside an attacker-controlled page. For example, an endpoint that performs state changes (such as changing a user’s email or toggling a setting) may be targeted: the attacker embeds that endpoint URL in an iframe and overlays invisible controls, so a logged-in user with valid Basic Auth credentials unknowingly submits requests.

AdonisJS does not enable clickjacking protections by default, so applications must explicitly set HTTP response headers such as X-Frame-Options or Content-Security-Policy frame-ancestors. Without these protections, endpoints protected only by Basic Auth remain vulnerable to clickjacking because the browser will include the credentials regardless of the embedding context. Attackers do not need to bypass Basic Auth; they rely on the browser sending credentials automatically and the application not restricting how its pages can be embedded.

Basic Auth-Specific Remediation in Adonisjs — concrete code fixes

To mitigate clickjacking in AdonisJS when using Basic Auth, set security headers on all responses and ensure sensitive actions require explicit verification beyond relying on the browser’s automatic credential submission. Below are concrete examples for an AdonisJS application using Basic Auth middleware.

Example Basic Auth middleware in AdonisJS

// start/hooks.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export const basicAuth = async (ctx: HttpContextContract, next: () => Promise) => {
  const username = ctx.request.header('authorization')?.split(' ')[1]
  const password = ctx.request.header('authorization')?.split(' ')[1] // simplified; decode properly in practice
  const valid = await validateBasicCredentials(username, password) // implement your validation

  if (!valid) {
    ctx.response.header('WWW-Authenticate', 'Basic realm="Secure Area"')
    ctx.response.status = 401
    return ctx.response.send('Unauthorized')
  }

  await next()
}

async function validateBasicCredentials(username: string | undefined, password: string | undefined): Promise {
  if (!username || !password) return false
  // Replace with secure lookup and constant-time comparison
  return username === process.env.BASIC_AUTH_USER && password === process.env.BASIC_AUTH_PASS
}

Setting X-Frame-Options header

Add an HTTP middleware that sets X-Frame-Options to DENY or SAMEORIGIN on every response. This prevents the browser from rendering responses inside an iframe regardless of authentication context.

// start/middleware/set_x_frame_options.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export const setXFrameOptions = async (ctx: HttpContextContract, next: () => Promise) => {
  await next()
  ctx.response.header('X-Frame-Options', 'DENY')
}

Register the middleware in start/kernel.ts under the global middleware list so it applies to all requests.

Setting Content-Security-Policy frame-ancestors

For modern browsers, use Content-Security-Policy with frame-ancestors to allow embedding only from trusted sources or none at all. This is more flexible than X-Frame-Options.

// start/middleware/set_csp.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export const setCsp = async (ctx: HttpContextContract, next: () => Promise) => {
  await next()
  ctx.response.header(
    'Content-Security-Policy',
    "default-src 'self'; frame-ancestors 'none'" // or 'self' https://trusted.example.com
  )
}

Defense in depth for sensitive actions

Even with headers in place, require explicit verification for state-changing operations inside your route handlers. For example, require a custom confirmation header or re-authentication for critical endpoints when Basic Auth is used, acknowledging that Basic Auth sends credentials automatically with each request and cannot be easily revoked per request without additional controls.

// routes.ts
import Route from '@ioc:Adonis/Core/Route'
import setCsp from './middleware/set_csp'
import setXFrameOptions from './middleware/set_x_frame_options'

Route.group(() => {
  Route.post('/account/change-email', async ({ request, response }) => {
    // Require a custom anti-CSRF token or re-authentication for sensitive actions
    const confirmation = request.header('x-confirm')
    if (confirmation !== 'confirm-change-email') {
      return response.badRequest({ error: 'Missing or invalid confirmation' })
    }
    // proceed with change
  }).middleware([setCsp, setXFrameOptions])
}).prefix('api')

Frequently Asked Questions

Does using Basic Auth alone protect against clickjacking in AdonisJS?
No. Basic Auth provides credentials but does not prevent a browser from automatically including those credentials when your app’s URLs are embedded in an iframe. You must explicitly set anti-clickjacking headers such as X-Frame-Options or Content-Security-Policy frame-ancestors to protect against clickjacking.
Should I use X-Frame-Options or Content-Security-Policy frame-ancestors in AdonisJS?
Use both for defense in depth. X-Frame-Options is widely supported for simple DENY or SAMEORIGIN policies. Content-Security-Policy frame-ancestors is more flexible and recommended for modern applications; it allows exceptions for specific origins and can be combined with X-Frame-Options safely.