Phishing Api Keys in Feathersjs with Cockroachdb
Phishing Api Keys in Feathersjs with Cockroachdb — how this specific combination creates or exposes the vulnerability
FeathersJS is a framework for creating REST and real-time APIs with minimal configuration. When integrating FeathersJS with CockroachDB, a PostgreSQL-compatible distributed SQL database, developers often rely on connection strings that include high-privilege credentials. If these credentials are exposed—such as through client-side code, logs, or misconfigured error messages—they can be phished or leaked to unauthorized parties.
In a FeathersJS service file (e.g., src/services/items/items.class.js), it is common to pass a database URL directly to an ORM or query builder. If the application is running in a client-side context (for example, when using browser builds or bundling server and client code together), a hardcoded CockroachDB URL can be extracted from the JavaScript bundle. Attackers can then use these credentials to connect directly to the database, bypassing application-level controls.
The risk is compounded when the FeathersJS application exposes raw errors that include connection strings or query details. An attacker can trigger verbose errors by sending malformed queries or by exploiting unhandled promise rejections, inadvertently revealing CockroachDB credentials in stack traces or HTTP responses. Since CockroachDB accepts SQL wire protocol connections over the network, exposed credentials can lead to data exfiltration, data modification, or lateral movement within a cloud VPC if the database is improperly network-isolated.
Another vector involves service discovery and configuration files. If FeathersJS loads configuration from environment variables at build time (for example, via Webpack DefinePlugin), and those variables contain CockroachDB connection strings, they can be included in source maps or client-side bundles. Phishing campaigns targeting developers might attempt to harvest these credentials from public repositories or logs, especially when teams store configuration in version control without proper secret management.
Additionally, when using unauthenticated or misconfigured endpoints, an attacker can probe the FeathersJS application to enumerate services and infer database connection patterns. The combination of an open FeathersJS HTTP API and a CockroachDB instance accessible from the application host increases the attack surface. Security checks that test for unauthenticated data exposure and unsafe consumption are effective at detecting such integration risks, highlighting the need to separate application logic from database credentials.
Cockroachdb-Specific Remediation in Feathersjs — concrete code fixes
To secure a FeathersJS application using CockroachDB, keep database credentials server-side and never expose them to the client. Use server-side services to mediate all database operations, and configure your ORM or query builder to read connection details from runtime environment variables injected by the hosting platform.
Below are concrete, server-side examples for a FeathersJS service that connects to CockroachDB using pg (the native PostgreSQL driver), which is compatible with CockroachDB.
Secure FeathersJS service with runtime-injected configuration
// src/services/items/items.service.js
const { Service } = require('feathersjs');
const { Pool } = require('pg');
class ItemsService extends Service {
constructor(options) {
super(options);
// Read from environment at runtime; do not bundle with client code
this.pool = new Pool({
connectionString: process.env.COCKROACHDB_CONNECTION_STRING,
ssl: {
rejectUnauthorized: true
}
});
}
async find(params) {
const { rows } = await this.pool.query('SELECT id, name, created_at FROM items WHERE user_id = $1', [params.user.id]);
return rows;
}
async get(id, params) {
const { rows } = await this.pool.query('SELECT id, name, updated_at FROM items WHERE id = $1 AND user_id = $2', [id, params.user.id]);
if (rows.length === 0) {
throw new Error('not-found');
}
return rows[0];
}
async create(data, params) {
const { rows } = await this.pool.query(
'INSERT INTO items (name, user_id) VALUES ($1, $2) RETURNING id, name, created_at',
[data.name, params.user.id]
);
return rows[0];
}
}
module.exports = function() {
const app = this;
app.use('/items', new ItemsService({
name: 'items'
}));
};
Ensure that COCKROACHDB_CONNECTION_STRING is provided by your deployment environment (e.g., Kubernetes secrets, Docker secrets, or a serverless platform’s environment configuration). The connection string should use the postgresql:// scheme and include ?sslmode=verify-full to enforce TLS certificate validation, which is critical when connecting to a distributed CockroachDB cluster.
Using an ORM with server-side configuration only
If you prefer an ORM, configure it server-side and avoid exporting models or instances to the client bundle. For example, using an Object-Relational Mapping approach with Knex (which works with CockroachDB):
// src/db.js (server-side only)
const { Client } = require('pg');
const client = new Client({
connectionString: process.env.COCKROACHDB_CONNECTION_STRING,
ssl: {
rejectUnauthorized: true
}
});
async function connect() {
await client.connect();
}
module.exports = { client, connect };
// src/services/items/items.service.js
const { client } = require('../db');
class ItemsServiceSecure {
constructor(options) {
this.options = options;
}
async find(params) {
const result = await client.query(
'SELECT id, name FROM items WHERE user_id = $1',
[params.user ? params.user.id : null]
);
return result.rows;
}
}
module.exports = ItemsServiceSecure;
These patterns ensure that CockroachDB credentials remain on the server and are never serialized into client-side artifacts. They also align with the principle of least privilege: ensure the database user used by FeathersJS has only the permissions required for its operations (e.g., SELECT, INSERT, UPDATE on specific tables).