Security Architecture
PULSE security model and implementation
Security Architecture
PULSE implements defense-in-depth security across all layers.
Authentication & Authorization
API Key Management
// API key format: pulse_prod_xxxxx
const API_KEY_PREFIX = 'pulse_prod_'
const API_KEY_LENGTH = 32 // + prefix
// Key rotation
// 1. Generate new key
// 2. Deploy with both keys
// 3. Clients update
// 4. Revoke old key
Authentication Flow
1. Client sends: Authorization: Bearer pulse_prod_xxxxx
2. Server checks KV cache
3. If miss: Query D1, cache result (60s TTL)
4. Verify key belongs to site_id
5. Return 401 if invalid
Session Management
// HTTPOnly cookies
response.headers.set('Set-Cookie',
`session=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`
)
// SessionID validation on every request
const session = await validateSession(sessionId)
Network Security
HTTPS/TLS
- Cloudflare handles automatic TLS
- Minimum TLS 1.2
- HSTS headers enabled
- Certificate pinning (optional)
CORS Configuration
const origin = request.headers.get('Origin')
const ALLOWED_ORIGINS = [
'https://app.example.com',
'https://analytics.example.com'
]
if (!ALLOWED_ORIGINS.includes(origin)) {
return new Response('Forbidden', { status: 403 })
}
Rate Limiting
Per-IP rate limits:
// Auth endpoint: 10 requests/minute
const limitResult = await env.RATE_LIMITER.limit({
key: `${clientIp}/auth`
})
if (!limitResult.success) {
return new Response(JSON.stringify({
success: false,
error: 'Rate limit exceeded'
}), { status: 429 })
}
Data Protection
Encryption at Rest
- D1 database: Encrypted at rest
- KV: Encrypted at rest
- R2: Encrypted at rest
Encryption in Transit
- TLS 1.3+ for all connections
- HKDF for key derivation
- No unencrypted HTTP
Data Masking
Sensitive data handling:
// Password hashing
const hash = await crypto.subtle.digest('SHA-256', password)
// PII redaction
const redactedEmail = '***@example.com'
// Token masking in logs
console.log(`Token: ${token.substring(0, 8)}...`)
Input Validation
Strict Validation
// Always validate at boundary
const schema = z.object({
email: z.string().email(),
age: z.number().int().min(0).max(150)
})
const validated = schema.parse(input)
SQL Injection Prevention
// Good: Parameterized queries
db.prepare('SELECT * FROM users WHERE id = ?').bind(userId).first()
// Bad: String concatenation
db.prepare(`SELECT * FROM users WHERE id = ${userId}`).first()
XSS Prevention
// Sanitize HTML output
const sanitized = DOMPurify.sanitize(userContent)
// Use CSP headers
response.headers.set('Content-Security-Policy',
"default-src 'self'; script-src 'self' 'unsafe-inline'"
)
OWASP Top 10 Protection
| Vulnerability | Protection |
|---|---|
| Injection | Parameterized queries, input validation |
| Broken Auth | API key validation, rate limiting, HTTPOnly cookies |
| Sensitive Data | Encryption at rest + transit, masking |
| XML External Entities | JSON only, no XML parsing |
| Broken Access Control | Site-level isolation, row-level security |
| Security Misconfiguration | Secrets via environment, no defaults |
| XSS | HTML sanitization, CSP headers |
| Insecure Deserialization | JSON validation, no unsafe parsing |
| Using Components with Known Vulns | Regular npm audit, dependency updates |
| Insufficient Logging | Request logging, audit trail |
GDPR Compliance
Data Retention
// Automatic deletion after retention period
DELETE FROM events
WHERE created_at < NOW() - INTERVAL '90 days'
Right to Deletion
// User data deletion
DELETE FROM events WHERE user_id = ?
DELETE FROM users WHERE user_id = ?
DELETE FROM cohort_members WHERE user_id = ?
Data Portability
// Export user data
SELECT * FROM events WHERE user_id = ?
ORDER BY timestamp DESC
Secrets Management
No Hardcoded Secrets
// Bad
const API_KEY = "sk-proj-xxxxx"
// Good
const API_KEY = process.env.API_SECRET
if (!API_KEY) throw new Error('API_SECRET not configured')
Secret Rotation
# Update secret
wrangler secret put API_SECRET --env production
# Verify new secret works
# Then revoke old secret
wrangler secret delete API_SECRET_OLD
Audit Logging
Track all access:
const auditLog = {
timestamp: Date.now(),
userId: user.id,
action: 'api_call',
endpoint: path,
status: response.status,
ipAddress: clientIp
}
await logAudit(auditLog)
Third-Party Security
Dependency Management
# Check for vulnerabilities
npm audit
# Update dependencies
npm update
# Review new versions
npm list
Supply Chain Security
- Minimize dependencies
- Use official SDKs only
- Regular security audits
- Pin dependency versions
Production Hardening
Environment Isolation
# Development
[env.development]
vars = { DEBUG = "true" }
# Production
[env.production]
vars = { DEBUG = "false" }
Error Handling
Never expose sensitive info:
// Bad
return { error: "Database connection failed: " + err.message }
// Good
return { error: "An error occurred. Please try again." }
console.error("DB Error:", err) // Log details separately
Health Checks
Verify security posture:
curl https://api.example.com/health/ready
# Checks:
# - Database connectivity
# - KV connectivity
# - API secret configured
# - TLS working
Security Best Practices
-
Principle of Least Privilege
- Grant minimum required permissions
- API keys per-site, not global
-
Defense in Depth
- Multiple security layers
- Fail securely
-
Continuous Monitoring
- Log all access
- Alert on anomalies
-
Regular Updates
- Patch dependencies
- Update runtime
-
Incident Response
- Have runbook
- Practice procedures
Security Checklist
- API keys securely stored
- HTTPS/TLS enabled
- Rate limiting active
- Input validation in place
- SQL injection prevention
- XSS prevention enabled
- CORS configured
- Secrets not in code
- Audit logging enabled
- Error messages sanitized
Next Steps
- Deployment — Secure deployment
- Compliance — GDPR/compliance
- Components — Security in components
Last updated: April 3, 2026