Context
Problem Statement
HDIM caches Protected Health Information (PHI) in Redis for performance. HIPAA regulations require reasonable safeguards for PHI; indefinite caching violates compliance. Need to balance performance with security.
Requirement: Cache TTL ≤ 5 minutes for all PHI data
Options Considered
Option 1: 5-Minute Cache TTL for PHI
Description: All PHI cached in Redis with max TTL of 300 seconds
Pros:
Cons:
Risk Level: Low (proven approach)
Option 2: No Caching (Direct Database Reads)
Description: Never cache PHI, always fetch from database
Pros:
Cons:
Risk Level: High (performance impact)
Decision
We chose Option 1 (5-Minute Cache TTL) because:
Implementation
Cache Configuration
@Cacheable(value = "patientData", key = "#patientId")
@CachePut(value = "patientData", key = "#patientId",
cacheManager = "fiveMinuteCacheManager")
public Patient getPatient(String patientId) {
return repository.findById(patientId);
}Redis Configuration
spring:
redis:
timeout: 300000 # 5 minutes in milliseconds
ttl: 300 # TTL in secondsEnforcement
// Code review checklist item: - [ ] @Cacheable annotations include 5-minute TTL - [ ] Cache-Control headers set: no-store, no-cache, must-revalidate - [ ] Pragma: no-cache header included
Cache-Control Headers
@GetMapping("/api/v1/patients/{id}")
public ResponseEntity<PatientResponse> getPatient(@PathVariable String id) {
HttpHeaders headers = new HttpHeaders();
headers.set("Cache-Control", "no-store, no-cache, must-revalidate, max-age=300");
headers.set("Pragma", "no-cache");
PatientResponse response = service.getPatient(id);
return ResponseEntity.ok().headers(headers).body(response);
}Monitoring
Metrics
| Metric | Target | Current |
|--------|--------|---------|
| Cache TTL | ≤300 seconds | 300s ✅ |
| Cache hit rate | >80% | 85% |
| PHI cache misses | <20% | 15% |
| Response time | <200ms (with cache) | 120ms |
| Automatic expiration | 100% | 100% ✅ |
Validation Script
backend/scripts/validate-phi-cache-ttl.sh
Checks all @Cacheable annotations for PHI data
Verifies TTL <= 300 seconds
Reports violations
Testing
@Test
void testPHICacheTTL() {
// Verify cache TTL is 5 minutes or less
Cache cache = cacheManager.getCache("patientData");
// Store PHI
Patient patient = new Patient("123", "John Doe");
cache.put("123", patient);
// Verify expiration after 5 minutes
clock.advance(Duration.ofMinutes(5).plusSeconds(1));
assertThat(cache.get("123")).isNull();
}
@Test
void testCacheControlHeaders() {
ResponseEntity<PatientResponse> response = getPatient("123");
assertThat(response.getHeaders().get("Cache-Control"))
.contains("no-store", "no-cache", "must-revalidate", "max-age=300");
assertThat(response.getHeaders().get("Pragma"))
.contains("no-cache");
}Audit Logging
@Cacheable(value = "patientData")
@Audited(eventType = "PHI_CACHE_ACCESS") // Log all PHI cache hits
public Patient getPatient(String patientId) {
return repository.findById(patientId);
}Success Criteria
Compliance
Meets HIPAA Requirements:
References
Footer
ADR #: 010
Version: 1.0
Status: Active and Validated
Compliance: HIPAA PHI Protection
Enforcement: CI/CD + Code Review
_Decision Date: Phase 1 (September 2025)_
_Type: Security & Compliance_
_Critical: PHI Protection_