Cache Poisoning in Cassandra
How Cache Poisoning Manifests in Cassandra
Cache poisoning in a Cassandra-backed application typically occurs at the application layer, where a shared cache (e.g., Redis, Memcached, or even an in-process cache like Caffeine) stores database query results or computed responses. The vulnerability arises when cache keys are constructed using untrusted user input (e.g., URL parameters, headers) without proper sanitization or namespacing. An attacker can craft requests with malicious keys that either overwrite legitimate cached entries (poisoning) or cause the application to cache sensitive data under a predictable key, which the attacker can then retrieve.
In a Cassandra context, the attack surface often involves APIs that execute CQL queries with dynamic WHERE clauses. For example, an endpoint like GET /api/[email protected] might cache the user profile by using the raw email as part of the cache key. If the application does not validate or canonicalize the email parameter, an attacker could provide inputs like [email protected]%0aSet-Cookie: session=attacker (CRLF injection) or use special characters to break key partitioning, potentially poisoning the cache or causing cache collisions.
Another manifestation is through Cassandra's own row cache or key cache if misconfigured. While Cassandra's caches are internal and not directly exposed via API, an application that caches entire query results (e.g., a list of records) based on a user-supplied filter could inadvertently store sensitive data in a shared cache accessible to other users if the cache key does not include a tenant or user identifier.
Example Vulnerable Pattern (Java + Spring Data Cassandra):
@RestController
public class UserController {
@Autowired
private UserRepository userRepository;
@Autowired
private CacheManager cacheManager;
@GetMapping("/users")
public User getUser(@RequestParam String email) {
String cacheKey = "user:" + email; // Vulnerable: email is used directly
Cache cache = cacheManager.getCache("users");
User user = cache.get(cacheKey, User.class);
if (user == null) {
user = userRepository.findByEmail(email); // Executes CQL: SELECT * FROM users WHERE email = ?
cache.put(cacheKey, user);
}
return user;
}
}Here, if two users request [email protected] and [email protected]%20 (with trailing space), they may get different database results but the cache key differs, causing cache duplication. Worse, an attacker could request [email protected] and poison the cache for that key, serving the admin's data to anyone requesting that email.
Cassandra-Specific Detection
Detecting cache poisoning requires testing the API's handling of cache key generation and its impact on data confidentiality. middleBrick's scanning process includes an Input Validation check that probes for cache-related issues by sending crafted payloads and analyzing response consistency and cache-control headers. For Cassandra-backed APIs, the scanner looks for:
- Cache key manipulation: Submitting the same logical query with different encodings (e.g.,
[email protected]vsemail=a%40b.com) to see if the application returns different results, indicating separate cache entries for the same logical input. - Cache poisoning attempt: Caching a known value (e.g., a user's private note) via a crafted request, then accessing that value using a different user's context to see if the poisoned cache entry is served.
- Cache-Control analysis: Checking if sensitive responses are marked as cacheable (
Cache-Control: public) and whether they contain user-specific data. - Timing side channels: Measuring response times for cached vs. uncached requests to infer cache hits/misses for manipulated keys.
middleBrick's Data Exposure check also cross-references with the OpenAPI spec to identify parameters that might be used in cache keys. For example, if an operation has a query parameter user_id and the response includes other users' data when that parameter is tampered with, it flags a potential cache collision or poisoning.
Example Scan Command (CLI):
middlebrick scan https://api.example.com/users --checks input-validation,data-exposureThe scanner will automatically test for these patterns and, if found, report a prioritized finding with severity based on the potential impact (e.g., exposure of PII). The report includes the exact request/response pairs that demonstrate the issue, mapped to the relevant OWASP API Top 10 category (API4:2023 — Unrestricted Resource Consumption or API5:2023 — Broken Function Level Authorization, depending on context).
Cassandra-Specific Remediation
Remediation focuses on secure cache key design, input validation, and leveraging Cassandra's features to minimize risk. The primary goal is to ensure that cache keys are both unique per user/tenant and resistant to manipulation.
1. Namespace Cache Keys with User Context: Always include a user identifier (e.g., authenticated user ID, tenant ID) in the cache key. Do not rely solely on request parameters that can be controlled by the attacker.
// Fixed: Use authenticated user's ID from security context
@GetMapping("/users")
public User getUser(@RequestParam String email, Principal principal) {
String userId = principal.getName(); // From JWT/session
String cacheKey = String.format("user:%s:%s", userId, email); // Namespaced
Cache cache = cacheManager.getCache("users");
User user = cache.get(cacheKey, User.class);
if (user == null) {
user = userRepository.findByEmail(email);
// Additional check: ensure the returned user belongs to the requester
if (!user.getOwnerId().equals(userId)) {
throw new AccessDeniedException("Not authorized");
}
cache.put(cacheKey, user);
}
return user;
}2. Canonicalize and Validate Inputs: Normalize all user-supplied values used in cache keys (e.g., trim whitespace, lowercase emails) and validate against expected patterns.
String normalizedEmail = email.trim().toLowerCase();
if (!normalizedEmail.matches("^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$")) {
throw new IllegalArgumentException("Invalid email");
}
String cacheKey = String.format("user:%s:%s", userId, normalizedEmail);3. Use Cassandra's Native Security Features: While Cassandra does not directly prevent application-layer cache poisoning, its role-based access control (RBAC) and row-level security (via CREATE ROLE and GRANT) can limit the blast radius. Ensure the database user used by the application has only the necessary permissions (e.g., SELECT on specific tables) and that queries use prepared statements to avoid CQL injection that could lead to broader data exposure.
// In Cassandra, create a role with restricted permissions
CREATE ROLE api_user WITH PASSWORD = '***' AND LOGIN = true;
GRANT SELECT ON KEYSPACE myapp TO api_user;
// Application uses this role in its DataStax driver configuration4. Configure Caching at the Cassandra Layer Cautiously: If using Cassandra's row cache or key cache, ensure it is enabled only for tables with static, non-sensitive data. Avoid caching tables that store user-specific information. Configure cassandra.yaml appropriately:
row_cache_size_in_mb: 0 # Disable row cache for sensitive tables
key_cache_size_in_mb: 100 # Use key cache only for lookup tables5. Set Appropriate Cache-Control Headers: For responses containing user-specific data, set Cache-Control: private, no-store to prevent shared caches (like CDNs or browser caches) from storing them.
@GetMapping("/users")
public ResponseEntity getUser(...) {
User user = ...;
return ResponseEntity.ok()
.cacheControl(CacheControl.private().noStore())
.body(user);
} After applying fixes, re-scan with middleBrick to verify the issue is resolved. The Pro plan's continuous monitoring can alert you if a future change reintroduces the vulnerability.
Compliance and Risk Context
Cache poisoning can lead to violations of multiple compliance frameworks:
| Framework | Relevant Control | How Cache Poisoning Violates It |
|---|---|---|
| OWASP API Top 10 | API4:2023 — Unrestricted Resource Consumption | Poisoning can cause excessive cache storage or retrieval of unauthorized data. |
| PCI-DSS | Requirement 6.5.1 — Injection Flaws | If poisoning leads to exposure of cardholder data via cache collision. |
| GDPR | Article 32 — Security of Processing | Failure to protect personal data from unauthorized access via cache manipulation. |
| SOC 2 | CC6.1 — Logical and Physical Access Controls | Inadequate controls leading to data leakage. |
middleBrick's scoring maps findings to these frameworks, helping you prioritize remediation based on compliance impact. A cache poisoning issue in a Cassandra-backed API that stores PII would typically score as High (70–89) due to the direct risk of data exposure.