Package securecache provides a security wrapper for any httpcache.Cache implementation, adding:
- SHA-256 Key Hashing (always enabled) - Cache keys are hashed before storage to prevent key enumeration
- AES-256-GCM Encryption (optional) - Cached data is encrypted when a passphrase is provided
- ✅ Key Privacy: All cache keys are hashed with SHA-256 before storage
- ✅ Data Encryption: Optional AES-256-GCM encryption for cached responses
- ✅ Authenticated Encryption: GCM mode provides both confidentiality and authenticity
- ✅ Key Derivation: Uses scrypt for strong key derivation from passphrase
- ✅ Transparent: Works with any
httpcache.Cacheimplementation - ✅ Zero Dependencies: Uses only Go standard library and
golang.org/x/crypto
go get github.com/sandrolain/httpcache/wrapper/securecacheimport (
"github.com/sandrolain/httpcache"
"github.com/sandrolain/httpcache/wrapper/securecache"
)
// Wrap any cache backend with key hashing
cache := httpcache.NewMemoryCache()
secureCache, err := securecache.New(securecache.Config{
Cache: cache,
// No passphrase = only key hashing, no encryption
})
if err != nil {
panic(err)
}
transport := httpcache.NewTransport(secureCache)
client := transport.Client()import (
"github.com/sandrolain/httpcache"
"github.com/sandrolain/httpcache/redis"
"github.com/sandrolain/httpcache/wrapper/securecache"
)
// Wrap Redis cache with encryption
redisCache := redis.NewWithClient(redisConn)
secureCache, err := securecache.New(securecache.Config{
Cache: redisCache,
Passphrase: "your-secret-passphrase-keep-it-safe",
})
if err != nil {
panic(err)
}
transport := httpcache.NewTransport(secureCache)
client := transport.Client()if secureCache.IsEncrypted() {
fmt.Println("Cache is using encryption")
} else {
fmt.Println("Cache is using key hashing only")
}All cache keys are hashed using SHA-256 before being passed to the underlying cache. This provides:
- Privacy: Original URLs/keys are not exposed in the cache backend
- Consistency: Same input always produces the same hash
- Security: Prevents enumeration of cached URLs
When a passphrase is provided:
-
Key Derivation: The passphrase is processed through scrypt with strong parameters:
- N=32768 (CPU/memory cost)
- r=8 (block size)
- p=1 (parallelization)
- 32-byte output (256-bit key for AES-256)
-
Encryption: Data is encrypted using AES-256 in GCM mode:
- Provides both confidentiality and authenticity
- Random nonce for each encryption operation
- Authentication tag prevents tampering
-
Storage Format:
[12-byte nonce][encrypted data + 16-byte auth tag]
-
Passphrase Management:
- Use a strong, random passphrase (at least 32 characters)
- Store passphrase securely (environment variable, secret manager)
- Never commit passphrases to version control
- Use the same passphrase across application restarts
-
Key Rotation:
- Changing the passphrase invalidates all existing cached data
- Plan for cache invalidation when rotating passphrases
- Consider a migration strategy if needed
-
Performance:
- Encryption adds ~1-2ms overhead per operation
- scrypt key derivation happens once at initialization
- Consider the trade-off between security and performance
-
Compliance:
- Use encryption for sensitive data (PII, tokens, etc.)
- Key hashing alone may be sufficient for public data
- Consult your security team for compliance requirements
- Public API responses
- Non-sensitive data
- Performance-critical applications
- Basic privacy protection
- User-specific data (with
CacheKeyHeaders) - Authentication tokens in responses
- PII (Personally Identifiable Information)
- GDPR/CCPA compliance requirements
- HIPAA-regulated healthcare data
- PCI DSS credit card information
See examples/security-best-practices/ for:
- Complete working example
- Multi-user caching with encryption
- Passphrase management
- Performance comparisons
Benchmarks on Apple M2 (results may vary):
| Operation | Without Encryption | With Encryption | Overhead |
|---|---|---|---|
| Set | ~100ns | ~1.2ms | +1.1ms |
| Get | ~80ns | ~1.0ms | +0.9ms |
| Delete | ~50ns | ~50ns | none |
Encryption overhead is primarily from:
- Random nonce generation (~50μs)
- AES-256-GCM encryption/decryption (~900μs for typical HTTP response)
- Changing the passphrase invalidates all cached data
- Encrypted data is ~12 bytes larger (nonce) + 16 bytes (auth tag)
- No built-in key rotation mechanism
- All instances must use the same passphrase to share cache
See the main LICENSE.txt file in the repository root.