Information Disclosure in Express with Cockroachdb
Information Disclosure in Express with Cockroachdb — how this specific combination creates or exposes the vulnerability
When an Express application interacts with CockroachDB, information disclosure can occur through multiple vectors tied to database error messages, connection handling, and query construction. CockroachDB, like other SQL databases, returns detailed error objects that often include schema names, table names, and sometimes even internal constraints. If these errors are passed directly to the client in an API response, an attacker can learn about the underlying database structure.
In Express, developers commonly write route handlers that execute SQL queries using string concatenation or improperly parameterized queries. For example, consider a route that fetches a user by ID from a CockroachDB table:
app.get('/user/:id', async (req, res) => {
const { id } = req.params;
const result = await db.query('SELECT * FROM users WHERE id = ' + id);
res.json(result.rows);
});
If the id parameter is malformed or references a non-existent row, CockroachDB may return an error such as pq: relation "users" does not exist or include column details in constraint violation messages. If error handling is missing or incomplete, Express may forward these raw errors to the client, exposing table and column names that would not otherwise be public.
Another common pattern is logging verbose request context, including query parameters and database responses, without sanitization. CockroachDB driver logs can inadvertently include sensitive information such as connection strings, database names, or query details if debug logging is enabled. When these logs are accessible through an exposed endpoint or insecure storage, they become a source of information leakage.
The combination of Express middleware that does not sanitize errors and CockroachDB’s verbose error reporting creates a scenario where an attacker can perform reconnaissance by probing endpoints with invalid inputs and observing detailed responses. This can reveal the presence of specific tables such as api_keys or sessions, and indicate whether the database uses schemas like public or custom namespaces.
Additionally, improper handling of asynchronous operations in Express can lead to partial data exposure. If a route initiates multiple CockroachDB queries and one fails, the application might still return data from earlier successful queries, inadvertently disclosing information that should remain private. This is particularly risky in administrative endpoints where multiple data sources are joined.
To mitigate these risks, it is essential to implement centralized error handling in Express, ensuring that database-specific details are never reflected in HTTP responses. All errors from CockroachDB should be caught, logged internally with sufficient context for debugging, and replaced with generic messages before being sent to the client.
Cockroachdb-Specific Remediation in Express — concrete code fixes
Secure integration with CockroachDB in Express requires strict error isolation, parameterized queries, and careful management of database metadata. The following examples demonstrate how to structure code to prevent information disclosure.
1. Parameterized Queries and Input Validation
Always use parameterized queries to avoid SQL injection and prevent malformed input from triggering verbose database errors:
app.get('/user/:id', async (req, res, next) => {
const { id } = req.params;
if (!Number.isInteger(Number(id))) {
return res.status(400).json({ error: 'Invalid user ID' });
}
try {
const result = await db.query('SELECT id, name FROM users WHERE id = $1', [id]);
res.json(result.rows[0] || {});
} catch (err) {
next(err);
}
});
2. Centralized Error Handling
Implement an error-handling middleware that strips database-specific details:
app.use((err, req, res, next) => {
if (err && err.message) {
// Log full error internally (e.g., to secure monitoring system)
console.error('[DB Error]', err.stack);
// Return generic response to client
return res.status(500).json({ error: 'Internal server error' });
}
next(err);
});
This ensures that CockroachDB errors such as pq: duplicate key value violates unique constraint "users_pkey" are not exposed to the client.
3. Safe Query Construction for Schema Awareness
If dynamic table or column names are necessary, validate them against a whitelist instead of interpolating directly:
const allowedColumns = ['id', 'name', 'email', 'created_at'];
app.get('/search', async (req, res, next) => {
const { column, value } = req.query;
if (!allowedColumns.includes(column)) {
return res.status(400).json({ error: 'Invalid column' });
}
try {
const query = `SELECT ${column} FROM profiles WHERE ${column} = $1`;
const result = await db.query(query, [value]);
res.json(result.rows);
} catch (err) {
next(err);
}
});
4. Secure Connection and Query Logging
Avoid logging raw query results or connection URIs. If logging is required, redact sensitive fields:
const originalQuery = db.query;
db.query = async function(query, values) {
console.log('[DB QUERY]', { query: query, values: values });
return originalQuery.call(this, query, values);
};
Ensure that any logging mechanism does not capture result rows containing PII or sensitive configuration data returned by CockroachDB.
5. Use of Prepared Statements and Connection Pooling
Configure the CockroachDB driver to use prepared statements and secure connection pooling to reduce the risk of information leakage through repeated query patterns:
const { Pool } = require('pg');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
module.exports = pool;
Using a pool with timeouts helps prevent connections from exposing state between requests and limits the window for potential information leakage.