Security Best Practices
Defense-in-depth security overview — encrypted storage, request signing, domain whitelisting, rate limiting, and actionable recommendations.
OptStuff is built with a defense-in-depth approach. Even if one layer is compromised, multiple additional barriers protect your assets.
Security Layers
| Layer | Protects Against | How |
|---|---|---|
| Encrypted Storage | Database breaches | AES-256-GCM encryption for secret keys at rest |
| Request Signing | Unauthorized usage | HMAC-SHA256 signature on every request |
| Signature Expiration | Replay attacks | Optional exp timestamp limits URL validity |
| Domain Whitelisting | Hotlinking & abuse | Two-layer domain control |
| Rate Limiting | Resource exhaustion | Per-key per-minute and per-day limits |
| Dual-Key System | Accidental exposure | Separates public identifiers from secrets |
Encrypted Storage
Secret keys are encrypted with AES-256-GCM before storage. The encryption key is derived via HKDF (RFC 5869) from a master secret stored in environment variables — separate from the database. Even if the database is compromised, attackers cannot obtain usable keys.
Request Signing
Every request must include an HMAC-SHA256 signature. The signature is a one-way function — intercepting it cannot reveal the secret key. Signatures are compared using constant-time comparison (timingSafeEqual) to prevent timing attacks.
See URL Signing for implementation details.
Signature Expiration
The optional exp parameter limits how long a signed URL is valid. Recommended expiration times depend on the use case — see URL Signing.
Domain Whitelisting
Two-layer domain control restricts both who can use the service (referer domains) and which images can be processed (source domains). Both settings are configured at the project level in Settings → Domain Security. Referer validation targets browser-based hotlinking specifically — requests without a Referer header are allowed since they indicate server-to-server calls or privacy-stripping policies, not hotlinking. See Domain Whitelisting for configuration and Referer Security Model for the trust model.
Rate Limiting
Per-key rate limits prevent abuse and contain the impact of compromised keys. In the current implementation, Redis outages cause rate limiting to fail open to preserve availability; monitor Redis health and alerting accordingly. See Rate Limiting for details.
Dual-Key System
Public keys (pk_...) are safe to expose in URLs. Secret keys (sk_...) never leave your server. Even if someone sees a signed URL, they cannot forge new requests.
Attack Protection Summary
| Attack | Protection |
|---|---|
| Brute Force | HMAC-SHA256 + 32-char signature makes collision infeasible |
| Replay Attacks | Signature expiration (exp) limits URL validity |
| Timing Attacks | timingSafeEqual constant-time comparison |
| Database Breach | AES-256-GCM encryption; keys useless without master key |
| Man-in-the-Middle | HTTPS + signature validation |
| Hotlinking | Referer domain whitelist |
| Unauthorized Sources | Source domain whitelist |
| Resource Exhaustion | Per-key rate limiting |
| SSRF | Image proxy validation/whitelisting and request sanitization (details) |
Secure Defaults
| Setting | Empty/Unset Behavior |
|---|---|
allowedSourceDomains | Production: reject all requests |
allowedRefererDomains | Allow all (no restriction) |
| API key expiration | No expiration (we recommend setting one) |
Technical Standards
| Standard | Usage |
|---|---|
| AES-256-GCM | Authenticated encryption for stored keys |
| HKDF (RFC 5869) | Cryptographic key derivation |
| HMAC-SHA256 | URL signature generation |
| Constant-time comparison | Timing-safe signature verification |
Production Checklist
Use this checklist before going live:
API Key Security
- Secret keys (
sk_...) stored in server-side environment variables only - No secret keys in frontend code, Git, or client-side bundles
-
.env/.env.localfiles added to.gitignore - API key expiration set for non-permanent integrations
- Unused or test keys revoked
Domain Security
-
allowedSourceDomainsexplicitly configured (empty list rejects all in production) -
allowedRefererDomainsconfigured if browser hotlinking is a concern - Development, staging, and production use separate projects or domain configs
URL Signing
- All signing happens server-side (Server Components, API Routes, or backend services)
- Appropriate
expexpiration set on signed URLs - No signing logic in client-side JavaScript
Infrastructure
- Redis health monitoring configured
- Rate limit alerts set (see Rate Limiting)
- HTTPS enforced on all endpoints
Environment Isolation
Use separate projects and API keys for each environment to prevent cross-environment leakage:
| Environment | Project | API Key | Source Domains |
|---|---|---|---|
| Development | my-app-dev | Dev key (short expiry) | Empty (allows all) |
| Staging | my-app-staging | Staging key | Same as production |
| Production | my-app | Production key | Explicitly listed |
This ensures a compromised development key cannot access production resources, and development traffic does not consume production rate limits.
Incident Response: Key Compromise
If an API key may have been exposed:
- Rotate immediately — Use the Rotate action in Dashboard → API Keys. This atomically revokes the old key and creates a new one.
- Update environment variables — Deploy the new secret key to all services that use it.
- Review request logs — Check Dashboard → Usage for suspicious traffic patterns during the exposure window.
- Audit access — Verify no unauthorized domains were added to the project's domain security settings.
- Consider expiration — For the new key, set a shorter expiration until you've confirmed the incident is contained.
Rotation is atomic — the old key stops working the moment the new key is created. Plan for a brief window where in-flight requests with the old key may fail.
Related Documentation
- URL Signing — Signature generation and code examples
- Domain Whitelisting — Domain configuration
- Rate Limiting — Rate limit configuration
- Key Management — API key lifecycle best practices
- SSRF Prevention — How the image proxy prevents Server-Side Request Forgery
Last updated on