Open Redirect in Feathersjs with Cockroachdb
Open Redirect in Feathersjs with Cockroachdb — how this specific combination creates or exposes the vulnerability
An Open Redirect in a Feathersjs service that uses Cockroachdb typically arises when a route accepts a user-supplied URL or location parameter and returns an HTTP redirect without validating the target. Because Feathersjs is a flexible framework, developers often add convenience endpoints that forward clients to stored or dynamic URIs, such as after authentication or as part of an OAuth flow. When those endpoints pull redirect values from a Cockroachdb-backed data store, the database may contain attacker-controlled values if input validation and schema constraints are weak.
Consider a Featherjs service that stores callback URLs for a multi-tenant application. A row in a Cockroachdb table might contain a column like redirect_uri. If the application trusts this stored value and uses it directly in a response (e.g., res.redirect(row.redirect_uri)) without ensuring it matches an allowlist, an authenticated or even unauthenticated attacker who can write to the table can induce victims to visit malicious sites. The risk is compounded when combined with other findings such as BOLA/IDOR, where an attacker can read or modify records belonging to other users, enabling them to replace a legitimate redirect URI with a phishing domain.
Because Cockroachdb is often used for distributed, multi-region applications, services may expose REST or GraphQL endpoints that return redirect targets based on complex joins across tenant tables. If authorization checks are incomplete, an attacker may enumerate valid redirect URIs across organizations or environments. The Feathersjs layer may not enforce strict schema validation on redirect fields, allowing formats like javascript:alert(1) or protocol-relative URLs (e.g., //evil.com/steal). Even when the application intends to restrict schemes to HTTP/HTTPS, inconsistent validation on read versus write paths can create a window for abuse.
Moreover, if the Cockroachdb schema includes columns that store hostnames or paths used to construct URLs, misconfigured default values or missing NOT NULL constraints can lead to empty or null values being interpreted as a safe fallback, which may still produce an open redirect depending on how Feathersjs handles undefined parameters. The framework does not inherently sanitize external data; it is the developer’s responsibility to ensure that any data sourced from Cockroachdb—and especially data that influences HTTP responses—is validated against a strict allowlist before being used in redirects.
Cockroachdb-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on strict validation, allowlisting, and separation of data from logic. In Feathersjs, always validate redirect parameters and stored URI fields against a precise allowlist of permitted hosts and schemes. Use a schema validator on the service layer regardless of Cockroachdb constraints, because database-level checks alone cannot prevent application-layer misuse.
Below is a robust example of a Feathersjs service hook that validates a redirect URI before use. It ensures the URI uses HTTPS, belongs to an approved host list, and is not a protocol-relative or javascript: URL. The hook runs on before and can also be applied to after when returning stored values that may be used by clients.
const redirectHosts = new Set(['app.yourcompany.com', 'auth.yourcompany.com']);
function validateRedirectUri(uri) {
try {
const url = new URL(uri);
if (url.protocol !== 'https:') {
throw new Error('Only HTTPS URIs are allowed');
}
if (!redirectHosts.has(url.hostname)) {
throw new Error('Hostname not allowed');
}
// Optionally enforce path prefixes
if (!url.pathname.startsWith('/callback')) {
throw new Error('Path not allowed');
}
return url.toString();
} catch (error) {
throw new Error('Invalid redirect URI');
}
}
app.hooks({
before: {
create: [context => {
if (context.data.redirectUri) {
context.data.redirectUri = validateRedirectUri(context.data.redirectUri);
}
return context;
}],
get: [context => {
if (context.result.redirectUri) {
context.result.redirectUri = validateRedirectUri(context.result.redirectUri);
}
return context;
}],
update: [context => {
if (context.data.redirectUri) {
context.data.redirectUri = validateRedirectUri(context.data.redirectUri);
}
return context;
}]
}
});
On the Cockroachdb side, enforce strong schema constraints and avoid storing raw redirect values without normalization. Use a CHECK constraint to ensure stored URIs conform to expected patterns, and prefer storing only paths or hostnames rather than full URIs when possible. Below is an example DDL for a tenant_redirects table that minimizes risk by storing only the path and hostname separately, then reconstructing the URI safely in application code.
CREATE TABLE tenant_redirects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
path TEXT NOT NULL CHECK (path ~ '^/[a-zA-Z0-9/_.-]*$'),
hostname TEXT NOT NULL CHECK (hostname ~ '^(app|auth)\.yourcompany\.com$'),
created_at TIMESTAMPTZ DEFAULT now()
);
In your Feathersjs service, reconstruct the URI only after validating both components, and use a strict join that respects tenant boundaries to prevent BOLA/IDOR. This approach ensures that even if an attacker can write to the table, they cannot introduce malicious schemes or hosts. Combine this with runtime scanning using middleBrick to detect open redirect patterns and verify that remediation aligns with findings; the CLI can be run with middlebrick scan <url> to validate the behavior of your endpoints in CI/CD or pre-deployment checks.