HIGH open redirectrestifyfirestore

Open Redirect in Restify with Firestore

Open Redirect in Restify with Firestore — how this specific combination creates or exposes the vulnerability

An open redirect in a Restify service that uses Firestore typically occurs when an endpoint accepts a user-controlled URL or host parameter and then redirects the client to that value without strict validation. Firestore is often used to store configuration such as allowed redirect targets, tenant-specific domains, or campaign landing pages. If the application reads a key from Firestore (for example, a campaign code) and uses that value to build a redirect, an attacker who can influence the Firestore document or supply a malicious reference can cause the application to redirect users to arbitrary destinations.

Consider a scenario where a Restify endpoint accepts a campaign code, retrieves a redirect URL from Firestore, and then responds with a 302 to that URL. If the Firestore document is compromised or the application trusts an unsanitized user-supplied code to select which document to read, the attacker can manipulate the stored URL or cause the server to read a malicious document. Even without compromising Firestore, insufficient validation of the server-side target before issuing the redirect creates an open redirect. The presence of Firestore can increase the attack surface because developers may assume that data stored in Firestore is inherently safe, leading to relaxed validation on values used for redirects.

In a black-box scan, middleBrick tests this class of issue by submitting candidate parameters that could trigger a redirect, observing response headers such as Location, and checking whether the redirect target reflects unsanitized input or references unexpected hosts. When Firestore-backed configurations are involved, the scanner does not need credentials; it probes the public endpoint to detect missing host allowlists and missing referer checks. This behavior is relevant to common web vulnerabilities and is mapped to the OWASP API Top 10 and related compliance frameworks.

Firestore-Specific Remediation in Restify — concrete code fixes

Remediation focuses on never trusting values used for redirects, whether they originate from Firestore or from user input. Validate and normalize all targets against a strict allowlist of hosts or paths. Do not concatenate or directly forward user input into a Location header.

Example: A Restify endpoint that uses Firestore to look up a campaign document and redirects to a stored URL, implemented safely.

const restify = require('restify');
const { initializeApp, cert } = require('firebase-admin/app');
const getFirestore = require('firebase-admin/firestore');

initializeApp({
  credential: cert({
    projectId: process.env.FIREBASE_PROJECT_ID,
    clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
    privateKey: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n'),
  }),
});

const server = restify.createServer();
server.use(restify.plugins.bodyParser());

const allowedHosts = new Set(['app.example.com', 'cdn.example.com']);

server.get('/campaign/:code', async (req, res, next) => {
  const code = req.params.code;
  if (!code || !/^[a-zA-Z0-9_-]{1,64}$/.test(code)) {
    return res.send(400, { error: 'invalid_campaign' });
  }

  const db = getFirestore();
  const docRef = db.collection('campaigns').doc(code);
  const snap = await docRef.get();

  if (!snap.exists) {
    return res.send(404, { error: 'not_found' });
  }

  const data = snap.data();
  const target = data.redirectUrl;

  try {
    const url = new URL(target);
    if (!allowedHosts.has(url.hostname)) {
      return res.send(400, { error: 'disallowed_host' });
    }
    res.clearHeader('Content-Type');
    res.header('Location', url.toString());
    return res.send(302);
  } catch (err) {
    return res.send(400, { error: 'invalid_url' });
  }
});

server.listen(8080, () => {
  console.log('Campaign redirect service listening on port 8080');
});

Key points in this example:

  • The campaign code is validated with a strict regex before Firestore lookup to prevent path traversal or injection via the document ID.
  • The stored redirectUrl is parsed as a URL and checked against an explicit allowlist of hostnames before being used in a Location header.
  • No user-controlled value is directly forwarded; the final redirect target is the normalized URL from the allowlist check.

If you prefer to avoid storing full URLs in Firestore, store only path segments or known keys and construct the final URL server-side:

const allowedPaths = new Set(['/promo/summer', '/promo/winter']);
const targetPath = data.redirectPath;
if (!allowedPaths.has(targetPath)) {
  return res.send(400, { error: 'disallowed_path' });
}
const finalUrl = 'https://app.example.com' + targetPath;
res.header('Location', finalUrl);
return res.send(302);

Additional defensive measures include logging unexpected document reads, rotating credentials for Firestore access, and using the middleBrick CLI to validate that your endpoints do not leak open redirects in unauthenticated scans.

Frequently Asked Questions

Why is Firestore used in the examples, and does it change how open redirects are detected?
Firestore is used as an example data store for configurations like redirect URLs or campaign targets. From a security perspective, the presence of Firestore does not change how open redirects are detected; scanners look for unvalidated redirects regardless of backend storage. The key is to validate and allowlist any value that influences the Location header, whether it comes from Firestore or another source.
Can relying on Firestore security rules alone prevent open redirects?
No. Firestore security rules control read/write access to documents but do not validate the semantic correctness of data used for redirects. An attacker who can write a malicious URL into a document (or cause the server to read an unintended document) can still cause an open redirect if the server does not validate the target. Always validate and restrict redirect targets on the server.