Open Redirect in Firestore
How Open Redirect Manifests in Firestore
Open Redirect vulnerabilities in Firestore applications typically arise from improper handling of user-supplied URLs in redirect logic. In Firestore contexts, this often occurs when applications use client-side routing or authentication flows that dynamically construct redirect URLs based on query parameters or database-stored values.
A common Firestore pattern involves storing redirect URLs in documents and using them for post-authentication navigation. Consider this vulnerable implementation:
const userDoc = await db.collection('users').doc(userId).get();
const redirectUrl = userDoc.data().redirectUrl;
res.redirect(redirectUrl);The vulnerability here is that redirectUrl comes directly from Firestore without validation. An attacker could modify their user document to store https://evil.com or a phishing URL, causing all users who authenticate to be redirected to malicious sites.
Another Firestore-specific pattern involves using collection names as redirect targets:
const collection = req.query.collection || 'default';
const doc = await db.collection(collection).doc('landing').get();
res.redirect(doc.data().url);This is vulnerable because an attacker can supply any collection name via the query parameter, potentially accessing documents they shouldn't see and extracting redirect URLs from arbitrary collections.
Firestore's security rules can also introduce redirect vulnerabilities when improperly configured. Consider rules that allow read access based on dynamic parameters:
match /redirects/{redirectId} {
allow read: if request.auth != null;
}Combined with client-side URL construction, this allows authenticated users to read any redirect document and potentially chain it with other vulnerabilities.
Mobile applications using Firestore often implement deep linking with redirects. A vulnerable pattern looks like:
const deepLink = await db.collection('deeplinks').doc(id).get();
window.location.href = deepLink.data().targetUrl;If targetUrl isn't validated against an allowlist, attackers can create malicious deep links that redirect users to phishing sites or execute script-based attacks through URL schemes.
Firestore-Specific Detection
Detecting Open Redirect vulnerabilities in Firestore applications requires examining both the database structure and application code. Start by auditing your Firestore collections for URL fields that might be used in redirects.
Collections to examine:
redirects- often contains URL fields liketargetUrl,destination,nextusers- may containpostLoginRedirect,callbackUrl,returnTosettings- might storedefaultRedirect,fallbackUrlmarketing- could containcampaignUrl,trackingLink
Use the Firestore CLI to export suspicious collections:
firebase firestore:export --collections=redirects,users/settings
# Then search exported JSON for URL patterns
jq '.[] | select(.targetUrl? != null)' redirects.jsonStatic analysis of your application code should look for patterns where Firestore data flows into redirect functions:
grep -r 'res\.redirect\|window\.location\.href' --include='*.js' --include='*.ts'
grep -A5 -B5 'redirect\|location' --include='*.js' --include='*.ts'middleBrick's Firestore-specific scanning identifies Open Redirect vulnerabilities by:
- Analyzing your OpenAPI spec to understand redirect endpoints
- Testing unauthenticated access to redirect collections
- Attempting parameter injection in redirect URLs
- Checking if redirect targets are validated against allowlists
The scanner tests common attack vectors like:
// Test for unvalidated redirects
GET /api/redirect?to=https://evil.com
// Test for Firestore document injection
GET /api/redirect?docId=malicious-documentmiddleBrick reports findings with severity levels based on the impact. A critical finding indicates that unauthenticated users can redirect to arbitrary domains, while a high finding suggests authenticated users can manipulate redirect targets to external domains.
Firestore-Specific Remediation
Remediating Open Redirect vulnerabilities in Firestore applications requires a defense-in-depth approach. The most effective strategy combines input validation, allowlisting, and secure defaults.
Implement URL validation using a strict allowlist approach:
const allowedDomains = [
'https://yourapp.com',
'https://app.yourapp.com',
'https://docs.yourapp.com'
];
function validateRedirectUrl(url) {
try {
const parsed = new URL(url);
return allowedDomains.some(domain =>
parsed.hostname === new URL(domain).hostname
);
} catch (e) {
return false;
}
}
// Secure implementation
const redirectUrl = userDoc.data().redirectUrl;
if (validateRedirectUrl(redirectUrl)) {
res.redirect(redirectUrl);
} else {
res.redirect('/default-dashboard');
}For Firestore security rules, implement read restrictions that prevent unauthorized URL access:
match /redirects/{redirectId} {
allow read: if
request.auth != null &&
(request.auth.token.email_verified == true ||
request.auth.token.phone_number_verified == true);
}
match /users/{userId} {
allow read: if request.auth.uid == userId;
allow update: if request.auth.uid == userId &&
validateRedirectUrl(resource.data.redirectUrl);
}Store only relative paths in Firestore and construct full URLs server-side:
// In Firestore: store '/dashboard' instead of full URL
const userDoc = await db.collection('users').doc(userId).get();
const path = userDoc.data().redirectPath || '/dashboard';
const fullUrl = new URL(path, process.env.BASE_URL).href;
res.redirect(fullUrl);Implement a redirect service with centralized validation:
class RedirectService {
constructor() {
this.allowedDomains = new Set([
'yourapp.com',
'app.yourapp.com'
]);
this.defaultRedirect = '/dashboard';
}
async getRedirect(userId, requestedPath) {
const userDoc = await db.collection('users').doc(userId).get();
const storedPath = userDoc.data().redirectPath;
const path = requestedPath || storedPath || this.defaultRedirect;
const fullUrl = new URL(path, process.env.BASE_URL).href;
if (!this.validateUrl(fullUrl)) {
return new URL(this.defaultRedirect, process.env.BASE_URL).href;
}
return fullUrl;
}
validateUrl(url) {
try {
const parsed = new URL(url);
return this.allowedDomains.has(parsed.hostname);
} catch {
return false;
}
}
}Add monitoring to detect suspicious redirect patterns:
functions.firestore
.document('redirects/{redirectId}')
.onWrite((change, context) => {
const newData = change.after.data();
if (newData && newData.targetUrl) {
admin.analytics().logEvent('redirect_created', {
url: newData.targetUrl,
userId: context.auth?.uid
});
}
});