Timing Attack in Cockroachdb
How Timing Attack Manifests in Cockroachdb
Timing attacks in CockroachDB exploit the fact that database operations take varying amounts of time based on secret data. When an attacker can measure response times, they can infer sensitive information about user credentials, access control decisions, or data existence.
The most common manifestation occurs during authentication. CockroachDB's default authentication mechanisms compare passwords using standard string comparison functions. Consider this vulnerable authentication pattern:
func authenticateUser(username, password string) bool {
storedPassword, err := getPasswordFromDB(username)
if err != nil {
return false
}
return password == storedPassword
}
When passwords differ in length, the comparison returns immediately. When they differ in content, the comparison stops at the first mismatch. An attacker measuring response times can determine password length and potentially reconstruct passwords character by character.
Another vulnerable pattern appears in authorization checks. CockroachDB's role-based access control (RBAC) often uses direct comparisons for permission validation:
func checkPermission(user User, resource string) bool {
allowedResources := getUserPermissionsFromDB(user.ID)
for _, res := range allowedResources {
if res == resource {
return true
}
}
return false
}
The loop exits early when finding a match, creating timing variations that reveal whether a resource exists in a user's permission set. An attacker can probe for valid resources by measuring response time differences.
SQL injection timing attacks also target CockroachDB's query execution engine. When an attacker injects conditional logic into queries, the database's execution plan and data retrieval patterns create measurable timing differences:
SELECT * FROM users WHERE username = 'admin' AND (SELECT COUNT(*) FROM secrets WHERE id = 1) = 1
If the secrets table exists and id=1 exists, the query takes longer than when it doesn't. Repeated probing with different IDs reveals table structure and data existence.
CockroachDB's distributed architecture adds complexity. Network latency between nodes can mask or amplify timing differences. An attacker needs to account for network jitter and use statistical analysis over many requests to detect meaningful patterns.
The problem intensifies with CockroachDB's serializable isolation level. Transactions that access different data take varying times based on data distribution across nodes. An attacker observing transaction commit times can infer which nodes contain specific data.
Cockroachdb-Specific Detection
Detecting timing attacks in CockroachDB requires both code analysis and runtime monitoring. The middleBrick scanner specifically tests for timing vulnerabilities through its black-box scanning methodology.
middleBrick's timing attack detection for CockroachDB includes several automated checks:
Authentication Timing Analysis - The scanner measures response times across multiple authentication attempts with systematically varied inputs. It looks for patterns where response times correlate with password length or character similarity.
Authorization Timing Testing - middleBrick probes permission endpoints with valid and invalid resource identifiers, measuring timing variations that could reveal access control structure.
SQL Query Timing - The scanner tests for timing side channels in SQL queries by measuring execution times for queries with conditional logic that should have constant execution time.
Network Timing Analysis - For distributed CockroachDB clusters, middleBrick analyzes timing patterns across multiple nodes to detect network-level timing leaks.
Code-level detection involves static analysis for vulnerable patterns:
// Vulnerable pattern - direct string comparison
if password == storedPassword {
return true
}
// Vulnerable pattern - early loop exit
for _, res := range allowedResources {
if res == resource {
return true
}
}
middleBrick's OpenAPI spec analysis can identify endpoints that handle authentication or authorization and flag them for timing analysis. The scanner cross-references spec definitions with runtime findings to provide comprehensive coverage.
Runtime monitoring complements scanning. Tools like pg_stat_statements equivalents for CockroachDB can track query execution times. Setting up monitoring for authentication endpoint response times helps detect ongoing timing attacks.
The scanner also tests for database-specific timing leaks:
// Test for timing leaks in SQL queries
SELECT * FROM users WHERE username = 'admin' AND (SELECT pg_sleep(0.1) WHERE id = 1) = 1
If the query takes longer when the condition is true, timing attacks are possible.
Cockroachdb-Specific Remediation
Remediating timing attacks in CockroachDB requires implementing constant-time operations and eliminating data-dependent execution paths. CockroachDB provides several native features to help secure against timing attacks.
Constant-Time Authentication - Replace vulnerable string comparisons with constant-time comparison functions:
import "crypto/subtle"
func authenticateUser(username, password string) bool {
storedPassword, err := getPasswordFromDB(username)
if err != nil {
return false
}
// Constant-time comparison
return subtle.ConstantTimeCompare(
[]byte(password),
[]byte(storedPassword)
) == 1
}
CockroachDB's Go client library includes timing-safe comparison utilities that work across distributed nodes.
Constant-Time Authorization - Use set operations that execute in constant time:
func checkPermission(user User, resource string) bool {
allowedResources := getUserPermissionsFromDB(user.ID)
// Use map lookup instead of linear search
permissions := make(map[string]struct{}, len(allowedResources))
for _, res := range allowedResources {
permissions[res] = struct{}{}
}
// Constant-time existence check
_, exists := permissions[resource]
return exists
}
SQL Query Hardening - Use prepared statements and avoid conditional logic that creates timing variations:
// Vulnerable - timing depends on data existence
SELECT * FROM users WHERE username = $1 AND EXISTS (SELECT 1 FROM secrets WHERE id = $2)
// Better - use JOIN with constant-time behavior
SELECT u.* FROM users u
LEFT JOIN secrets s ON s.id = $2
WHERE u.username = $1
CockroachDB-Specific Features - Leverage CockroachDB's built-in security features:
// Use role-based access control instead of application-level checks
CREATE ROLE IF NOT EXISTS api_user;
GRANT SELECT ON TABLE users TO api_user;
// Use stored procedures for sensitive operations
CREATE OR REPLACE FUNCTION check_permission(user_id INT, resource VARCHAR)
RETURNS BOOL AS $$
DECLARE
allowed BOOL;
BEGIN
SELECT EXISTS (
SELECT 1 FROM user_permissions
WHERE user_id = $1 AND resource = $2
) INTO allowed;
RETURN allowed;
END;
$$ LANGUAGE plpgsql;
Network-Level Protection - Configure CockroachDB to minimize network timing variations:
// Set consistent network timeouts
SET CLUSTER SETTING kv.bulk.slow_threshold = '1s';
SET CLUSTER SETTING kv.bulk.fast_threshold = '500ms';
// Use connection pooling to eliminate connection establishment timing
CREATE CONNECTION POOL api_connections (
MAX_CONNECTIONS = 50,
IDLE_TIMEOUT = '30s'
);
Application-Level Hardening - Add artificial delays to mask timing variations:
func constantTimeOperation(operation func() bool, maxDelay time.Duration) bool {
start := time.Now()
result := operation()
elapsed := time.Since(start)
// Add random delay to mask timing variations
jitter := time.Duration(rand.Int63n(int64(maxDelay)))
time.Sleep(jitter - elapsed)
return result
}
These remediation strategies, combined with regular middleBrick scanning, create defense-in-depth against timing attacks in CockroachDB environments.