Sql Injection in Cassandra
How SQL Injection Manifests in Cassandra
SQL injection in Cassandra differs fundamentally from traditional RDBMS attacks due to Cassandra's CQL (Cassandra Query Language) architecture. Unlike SQL databases that use structured query plans, Cassandra's distributed nature and query model create unique injection vectors that target both data retrieval and cluster coordination.
The most common Cassandra injection pattern occurs in IN clause manipulation. Consider this vulnerable code:
String userId = request.getParameter("user_id");
String query = "SELECT * FROM users WHERE user_id IN (" + userId + ")";
ResultSet results = session.execute(query);
An attacker can inject multiple values or operators:
user_id=1,2,3 OR 1=1
This returns all rows because 1=1 evaluates to true for every record. The distributed nature of Cassandra means this query executes across all nodes, potentially overwhelming the cluster.
Another critical vector involves ALLOW FILTERING exploitation. Cassandra normally prevents full table scans, but ALLOW FILTERING bypasses this protection:
user_id=1) ALLOW FILTERING; SELECT * FROM sensitive_data; --
This injects a second query that Cassandra executes, exposing data the application never intended to query. The distributed execution model means this attack can simultaneously target multiple nodes.
Collection manipulation represents a third vector. Cassandra supports sets, lists, and maps as column types. Attackers can exploit collection update syntax:
user_id=1] + { 'admin': true };
This modifies the user's role collection, potentially escalating privileges. The injection succeeds because Cassandra's CQL parser processes the collection syntax before validation.
Cassandra-Specific Detection
Detecting SQL injection in Cassandra requires understanding both CQL parsing and Cassandra's distributed execution model. Traditional SQL injection tools often miss Cassandra-specific patterns because they don't recognize CQL syntax or Cassandra's query coordination mechanisms.
middleBrick's Cassandra detection focuses on several key indicators:
Dynamic Query Construction: The scanner analyzes API endpoints for patterns that concatenate user input directly into CQL strings. It specifically looks for:
- String concatenation with user parameters in query construction
- Missing prepared statement usage
- Direct
session.execute()calls with interpolated values
ALLOW FILTERING Abuse: middleBrick flags any API that accepts parameters which could enable ALLOW FILTERING in generated queries. This includes detecting patterns where user input might control query structure or enable full table scans.
Collection Injection Patterns: The scanner tests for collection manipulation by sending specially crafted payloads that attempt to modify collection data through query injection. This includes testing for set/list/map injection syntax.
Distributed Query Analysis: middleBrick evaluates whether the API's query patterns could trigger distributed execution across Cassandra nodes. Queries that might cause cross-node coordination or excessive data transfer are flagged as high-risk.
LLM Endpoint Testing: For APIs that use Cassandra as a vector for AI/ML applications, middleBrick actively tests for prompt injection that could manipulate database queries through AI model interfaces. This includes testing for system prompt extraction that might reveal database credentials or query patterns.
The scanner executes these tests in 5-15 seconds without requiring database credentials, making it practical for continuous security monitoring in development and production environments.
Cassandra-Specific Remediation
Remediating SQL injection in Cassandra requires adopting Cassandra-native security patterns. The most effective approach combines prepared statements, query validation, and proper data modeling.
Prepared Statements: Always use prepared statements for user input. Cassandra's prepared statement mechanism separates query structure from data, preventing injection:
// Vulnerable
String query = "SELECT * FROM users WHERE user_id = " + userId;
ResultSet results = session.execute(query);
// Secure
PreparedStatement stmt = session.prepare("SELECT * FROM users WHERE user_id = ?");
BoundStatement bound = stmt.bind(userId);
ResultSet results = session.execute(bound);
Prepared statements provide multiple security benefits: they prevent injection, enable query plan caching, and reduce network overhead through statement reuse.
Query Validation: Implement strict validation for all user inputs, especially for IN clauses and collection operations:
public BoundStatement createSafeQuery(Session session, String userId) {
if (!isValidUserId(userId)) {
throw new IllegalArgumentException("Invalid user ID format");
}
PreparedStatement stmt = session.prepare("SELECT * FROM users WHERE user_id = ?");
return stmt.bind(userId);
}
private boolean isValidUserId(String userId) {
// Validate against expected format (numeric, UUID, etc.)
return userId.matches("\\d+") || userId.matches("\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12}");
}
Proper Data Modeling: Design your Cassandra schema to avoid queries that require ALLOW FILTERING. Use appropriate primary keys and clustering columns to support your query patterns without full table scans.
-- Good: Query-friendly design
CREATE TABLE users_by_id (
user_id text,
email text,
role text,
PRIMARY KEY (user_id)
);
-- Bad: Requires ALLOW FILTERING for common queries
CREATE TABLE users (
email text,
user_id text,
role text,
PRIMARY KEY (email)
);
-- Querying by user_id would require ALLOW FILTERING
Collection Security: When using collections, validate all modification operations:
public void safeUpdateRoles(Session session, String userId, Set<String> newRoles) {
if (!hasPermissionToUpdateRoles()) {
throw new SecurityException("Insufficient permissions");
}
PreparedStatement stmt = session.prepare(
"UPDATE users SET roles = ? WHERE user_id = ?"
);
BoundStatement bound = stmt.bind(newRoles, userId);
session.execute(bound);
}
API Layer Protection: Implement rate limiting and input size restrictions at the API layer to prevent denial-of-service through query injection:
@GetMapping("/users")
@RateLimit(limit = 100, window = 60)
public List<User> getUsers(@RequestParam List<String> userIds) {
if (userIds.size() > 20) {
throw new BadRequestException("Maximum 20 user IDs allowed");
}
PreparedStatement stmt = session.prepare(
"SELECT * FROM users WHERE user_id IN ?"
);
BoundStatement bound = stmt.bind(userIds);
return executeQuery(bound);
}Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |