HIGH open redirectnestjsfirestore

Open Redirect in Nestjs with Firestore

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

An open redirect in a NestJS application that uses Firestore typically arises when a route accepts a user-supplied URL or hostname and then forwards the client without validation. If the route also interacts with Firestore to look up tenant or campaign configuration (for example to decide which redirect rules apply), an attacker can supply a malicious Firestore document reference or a crafted request that causes the application to redirect to an arbitrary external domain.

Consider a NestJS controller that reads a redirect target from Firestore based on a provided campaign ID and then sends an HTTP Location header to the client:

@Controller('redirect')
export class RedirectController {
  constructor(private readonly firestore: FirestoreService) {}

  @Get(':campaignId')
  async redirect(@Param('campaignId') campaignId: string, @Query('url') url: string) {
    const campaign = await this.firestore.getCampaign(campaignId);
    if (!campaign) {
      throw new NotFoundException('campaign not found');
    }
    // Unsafe: using user-provided 'url' directly
    const target = url || campaign.defaultUrl;
    return { url: target };
  }
}

If campaign.defaultUrl or the url query parameter contains a fully qualified external URL and the application uses that value in a Location header without validation, the endpoint becomes an open redirect. An attacker could share a link like /redirect/abc123?url=https://evil.example.com and trick users into visiting a malicious site. Even when Firestore stores only internal identifiers, the application must validate the resolved destination because a compromised document or misconfigured rule could point to attacker-controlled infrastructure.

OWASP API Top 10 categorizes this as a security misconfiguration. In the context of middleBrick’s 12 checks, this would typically surface under Property Authorization and Input Validation, where the scanner verifies whether redirect targets are constrained to expected domains and whether authorization checks are applied consistently across Firestore reads and runtime behavior.

Firestore-Specific Remediation in Nestjs — concrete code fixes

Remediation focuses on strict allowlisting of domains and avoiding direct exposure of Firestore values as redirect targets. Always resolve the intended destination on the server, never pass raw user input or unchecked Firestore fields to the Location header.

1. Validate against an allowlist of permitted domains or paths. Resolve the final URL server-side and return only a safe path or a known-safe identifier to the client.

@Controller('safe-redirect')
export class SafeRedirectController {
  constructor(private readonly firestore: FirestoreService) {}

  @Get(':campaignId')
  async safeRedirect(
    @Param('campaignId') campaignId: string,
    @Query('returnTo') userReturnTo: string,
  ) {
    const campaign = await this.firestore.getCampaign(campaignId);
    if (!campaign) {
      throw new NotFoundException('campaign not found');
    }

    // Define allowed base URLs for this campaign
    const allowedHosts = new Set(['app.example.com', 'marketing.example.com']);

    // Resolve server-side destination
    let destination = campaign.defaultUrl;

    // If a user return path is provided, validate it
    if (userReturnTo) {
      const parsed = new URL(userReturnTo, `https://${request.hostname}`);
      if (!allowedHosts.has(parsed.hostname)) {
        throw new BadRequestException('returnTo not allowed');
      }
      destination = parsed.toString();
    }

    // Server-side final resolution ensures safety
    if (!destination.startsWith('https://app.example.com') && !destination.startsWith('https://marketing.example.com')) {
      throw new BadRequestException('invalid destination');
    }

    return { redirectTo: destination };
  }
}

2. Use Firestore security rules to restrict which documents can contain external URLs and enforce that only approved fields are readable by the client. In your rules, avoid allowing arbitrary fields to be used for redirects.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /campaigns/{campaignId} {
      allow read: if request.auth != null && request.auth.token.trusted == true;
      allow write: if request.auth != null && request.auth.token.admin == true;
      // Ensure only safe fields are exposed
      allow list: if resource.data.keys().hasAll(['name', 'defaultUrl']) 
                  && resource.data.defaultUrl.matches('^https://(app|marketing)\\.example\\.com/.*$');
    }
  }
}

3. In your NestJS service layer, encapsulate Firestore reads and URL resolution so controllers remain thin and validation is centralized:

@Injectable()
export class RedirectService {
  constructor(private readonly firestore: FirestoreService) {}

  async resolveSafeDestination(campaignId: string, userReturnTo?: string): Promise {
    const campaign = await this.firestore.getCampaign(campaignId);
    const allowedHosts = ['app.example.com', 'marketing.example.com'];
    const base = `https://${campaign.tenantHost}`;

    let destination = campaign.defaultUrl;

    if (userReturnTo) {
      const url = new URL(userReturnTo, base);
      if (!allowedHosts.includes(url.hostname)) {
        throw new HttpException('Invalid return URL', HttpStatus.BAD_REQUEST);
      }
      destination = url.toString();
    }

    if (!destination.startsWith(base)) {
      throw new HttpException('Destination not permitted', HttpStatus.BAD_REQUEST);
    }

    return destination;
  }
}

These steps ensure that even if a Firestore document is misconfigured or compromised, the application will not perform unauthorized redirects. By combining server-side resolution, strict allowlists, and secure Firestore rules, you mitigate open redirect risks while still leveraging Firestore for configuration.

Frequently Asked Questions

How does middleBrick detect open redirect risks in NestJS APIs that use Firestore?
middleBrick runs parallel security checks including Input Validation and Property Authorization. It examines whether redirect targets derived from Firestore are validated against an allowlist and whether user-controlled values are passed unchecked to Location headers, flagging missing validation as a high-severity finding with remediation guidance.
Can the free plan be used to scan a NestJS API that integrates Firestore for redirect configuration?
Yes, the Free plan provides 3 scans per month, which is sufficient for initial assessments of a NestJS + Firestore integration. For continuous monitoring across multiple environments, the Starter or Pro plans offer scheduled scans and CI/CD integration to detect regressions before deployment.