drizzle-team / drizzle-team/drizzle-orm

[Security/Medium]: Missing SSRF validation when connecting to external databases

Open
#5,803 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
35.8k
Forks
1.6k
Avg merge
2d 7h
Merged PRs (30d)
4

Description

## Description

Drizzle Kit (the CLI tool for Drizzle ORM) connects to external databases for introspection and studio operations without validating database URLs. When users configure database credentials with URLs, these URLs are used directly by database drivers (pg, postgres, mysql2, @libsql/client, etc.) without any validation.

While this is primarily a developer tool, it can enable Server-Side Request Forgery (SSRF) in scenarios where:

1. Drizzle Kit is used in CI/CD pipelines with untrusted or user-provided database URLs
2. Configuration files (drizzle.config.ts) are built dynamically from user input
3. Studio is exposed with untrusted configuration sources

## Steps to Reproduce

In drizzle-kit/src/cli/connections.ts, database URLs are used directly:

Line ~260: PostgreSQL with pg driver
const client = 'url' in credentials
? new pg.Pool({ connectionString: credentials.url, max: 1 }) // No URL validation
: new pg.Pool({ ...credentials, ssl, max: 1 });

Line ~630: MySQL with mysql2 driver
const connection = result.url
? await createConnection(result.url) // No URL validation
: await createConnection(result.credentials!);

In drizzle-kit/src/serializer/studio.ts, the studio proxy accepts arbitrary SQL queries:

Line ~750: Studio proxy endpoint
if (type === 'proxy') {
const result = await proxy({
...body.data,
params: body.data.params || [],
});
return c.json(JSON.parse(jsonStringify(result)));
}

An attacker could provide a database URL like:
- postgres://user:pass@169.254.169.254:5432/db (cloud metadata service)
- mysql://user:pass@internal.company.service/db (internal services)
- libsql://file:../../sensitive.db (path traversal to local files with libsql)

## Impact

While Drizzle Kit is primarily a developer tool run locally, SSRF is possible when:

1. CI/CD Pipelines: Automated workflows that introspect databases with dynamic configuration
2. Multi-tenant Platforms: SaaS tools that use Drizzle Kit for database operations per tenant
3. Studio Exposure: Studio servers accessible with untrusted config sources

Potential attacks:
- Access cloud metadata services (AWS IMDS, GCP metadata)
- Scan internal network services
- Read local files via SQLite/LibSQL file URLs
- Port scanning through connection timeout analysis

## Suggested Fix

Add URL validation before establishing database connections:

import net from 'net';

const isSafePublicUrl = (url: string): boolean => {
try {
const parsed = new URL(url);

// Block localhost and private IPs
if (['localhost', '127.0.0.1', '::1', '0.0.0.0'].includes(parsed.hostname)) {
return false;
}

// Block private IP ranges
const hostname = parsed.hostname;
if (net.isIP(hostname)) {
// Check for private IPv4 ranges
if (net.isIPv4(hostname)) {
const parts = hostname.split('.').map(Number);
if (parts[0] === 10 ||
(parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) ||
(parts[0] === 192 && parts[1] === 168)) {
return false;
}
}
// IPv6 private ranges could also be checked
}

// Block path traversal in file URLs
if (parsed.protocol === 'file:') {
const filePath = parsed.pathname;
if (filePath.includes('..')) {
return false;
}
}

// Block cloud metadata endpoints
if (['169.254.169.254', '[fd00:ec2::254]', '[link-local]'].includes(hostname)) {
return false;
}

return true;
} catch {
return false;
}
};

// Apply validation in preparePostgresDB, connectToMySQL, etc.
const client = 'url' in credentials
? (() => {
if (!isSafePublicUrl(credentials.url)) {
throw new Error('Database URL validation failed: ' + credentials.url);
}
return new pg.Pool({ connectionString: credentials.url, max: 1 });
})()
: new pg.Pool({ ...credentials, ssl, max: 1 });

For Studio, consider adding:
- Authentication for the studio proxy endpoint
- Rate limiting
- Allowlist of safe hosts (configurable via env var)

## Environment

- Version: 0.32.1 (from drizzle-orm/package.json)
- OS: macOS
- Node.js: v20+

## References

- OWASP SSRF: https://owasp.org/www-community/attacks/Server_Side_Request_Forgery
- AWS IMDS: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.