HIGH clickjackinghapibasic auth

Clickjacking in Hapi with Basic Auth

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

Clickjacking is a client-side UI redress attack where an attacker tricks a user into interacting with a hidden or disguised element inside an invisible or misleading frame. In Hapi, when endpoints that require Basic Auth are rendered in a browser context (for example, returning HTML that includes forms or links), the combination can expose the application to clickjacking if framing protections are missing.

With Basic Auth, credentials are sent in the Authorization header on every request. If a Hapi route returns an HTML page and does not set appropriate Content-Security-Policy frame-ancestors or X-Frame-Options headers, an attacker can embed that page inside an <iframe> on a malicious site. Even though the browser will typically show a login dialog for the protected resource when credentials are missing, if the user already has valid Basic Auth credentials cached for that origin (for example, the user previously entered them), the embedded page may load within the attacker’s frame. The attacker can then overlay invisible controls or misleading UI over the framed content, causing the user to perform unintended actions such as changing settings or submitting forms while authenticated.

This risk is particularly relevant when Hapi serves both API and HTML responses from the same route or when CORS and Vary headers are misconfigured. For instance, an endpoint that returns JSON for programmatic use but also serves an HTML view can be framed if the browser treats the response as renderable content. Basic Auth does not inherently prevent framing; it only carries credentials. Without explicit anti-framing controls, an attacker can leverage the authenticated session to perform actions on behalf of the user, potentially leading to unauthorized changes or data exposure.

Consider a Hapi route that uses Basic Auth and returns an HTML page with a form to update email preferences. If the page is loaded inside an attacker-controlled frame and the user is already authenticated, the attacker can position a transparent button over the form’s submit action. The user believes they are interacting with the attacker’s page, but they are actually submitting the form in the hidden frame with their authenticated credentials. Because the request includes the Basic Auth header, the server processes it as a legitimate action from the authenticated user.

To assess this risk, scans examine whether responses include frame-limiting headers and whether pages that carry Basic Auth credentials are safe to embed. Findings highlight missing defenses and provide guidance on implementing frame protection to ensure that authenticated pages cannot be embedded by unauthorized sites.

Basic Auth-Specific Remediation in Hapi — concrete code fixes

Mitigating clickjacking for Hapi routes that use Basic Auth requires explicit framing controls and secure defaults for authenticated responses. The primary defenses are Content-Security-Policy frame-ancestors and, when needed, X-Frame-Options. These headers must be applied to responses that can be rendered in a browser, especially when credentials are present.

Below are concrete Hapi examples that show how to add security headers to routes, including a route that uses Basic Auth via the hapi-auth-basic plugin.

Example 1: Hapi route with Basic Auth and security headers

const Hapi = require('@hapi/hapi');
const bcrypt = require('bcrypt');
const auth = require('hapi-auth-basic');

const validate = (request, username, password, h) => {
  const isValid = checkUserPassword(username, password); // implement your check
  if (!isValid) {
    return { isValid: false };
  }
  return { isValid: true, credentials: { username, role: 'user' } };
};

const init = async () => {
  const server = Hapi.server({ port: 4000, host: 'localhost' });

  await server.register(auth);

  server.auth.strategy('simple', 'basic', {
    validateFunc: validate,
    passwordFunction: (username, password) => password // in practice, use a proper hash
  });

  server.route({
    method: 'GET',
    path: '/profile',
    options: {
      auth: 'simple',
      handler: (request, h) => {
        // For HTML responses, ensure framing protections are present
        const response = h.response('

Profile

...
'); response.header('Content-Security-Policy', "frame-ancestors 'none'"); response.header('X-Frame-Options', 'DENY'); return response; }, // Ensure responses that carry auth state cannot be framed plugins: { 'hapi-auth-basic': { mode: 'required', strategy: 'simple' } } } }); await server.start(); console.log('Server running on %s', server.info.uri); }; process.on('unhandledRejection', (err) => { console.error(err); process.exit(1); });

Example 2: Apply frame protections selectively based on request context

server.route({
  method: 'GET',
  path: '/public-page',
  handler: (request, h) => {
    const response = h.response('

Public

'); // Public pages may allow embedding by same-origin only response.header('Content-Security-Policy', "frame-ancestors 'self'"); return response; } }); server.route({ method: 'GET', path: '/settings', handler: (request, h) => { const response = h.response('

Settings

'); // Authenticated settings must not be framed response.header('Content-Security-Policy', "frame-ancestors 'none'"); response.header('X-Frame-Options', 'DENY'); return response; }, options: { auth: 'simple' } });

In these examples, the Content-Security-Policy frame-ancestors directive is the modern standard to control which origins may embed the page, while X-Frame-Options provides legacy browser compatibility. For routes that require Basic Auth, it is recommended to apply DENY or a restrictive policy to prevent any form of clickjacking. The Vary header should also be considered when caching authenticated responses to avoid unintended caching across users.

middleBrick’s scans detect missing frame-ancestors and X-Frame-Options headers on authenticated endpoints, helping you identify pages that could be framed. The findings include prioritized guidance so you can apply the correct headers and test that framing protections are effective.

Frequently Asked Questions

Does Basic Auth prevent clickjacking by itself?
No. Basic Auth transmits credentials in requests, but it does not prevent a page from being embedded in an iframe. You must add frame-limiting headers such as Content-Security-Policy frame-ancestors or X-Frame-Options to protect authenticated pages from clickjacking.
Should I apply frame protections to all Hapi routes, including APIs returning JSON?
If your Hapi routes return JSON only, framing is typically not relevant because browsers do not render JSON as pages. However, if any route can return HTML (or is served with a text/html Content-Type), apply Content-Security-Policy frame-ancestors and X-Frame-Options to prevent the page from being embedded, especially when credentials are present.