We run periodic internal security audits to verify that Postbox meets the standard we set for ourselves. This is a summary of our April 2026 audit.
Scope
The audit covered the full application stack:
- Authentication β session management, API keys, OAuth 2.1, two-factor
- Authorization β scope enforcement, entitlement checks, admin access
- Input validation β submission schemas, parameter handling, form data
- Injection vectors β SQL, XSS, SSRF, command injection
- Network security β CORS, CSP, rate limiting, security headers
- Data handling β tenant isolation, soft deletes, PII exposure
- Background processing β Oban worker safety, idempotency
- Dependencies β known vulnerabilities, outdated packages
- Infrastructure β container security, TLS, secrets management
Results
| Severity | Found | Resolved |
|---|---|---|
| Critical | 0 | β |
| High | 2 | 2 |
| Medium | 5 | 5 |
| Low | 5 | 4 |
| Info | 9 | β |
Zero critical findings. All high and medium issues were resolved in the same session as the audit.
What we found and fixed
SSRF protection at delivery time
Webhook destinations are validated when created, but DNS can change between save and delivery. We added DNS pinning β the webhook delivery system now resolves the hostname, validates the IP is not in a private range, and connects directly to the validated IP. This closes the window for DNS rebinding attacks targeting internal services.
Session hardening
Session cookies now enforce a server-side max_age, preventing indefinite
session replay in browsers that restore session cookies after restart.
Cache bounds
Our endpoint cache β used for fast submission validation β now has a TTL and a maximum entry count to prevent unbounded memory growth.
Input sanitization
Blog post code fence languages are now sanitized to prevent injection through
markdown content. Webhook destinations block http://localhost in
production.
Security headers
Added Referrer-Policy: strict-origin-when-cross-origin to prevent token
leakage in referrer headers. Tightened img-src CSP to remove the https:
wildcard. CSP form-action is now environment-specific.
Rate limiting
The rate limiter now reads the correct client IP from the X-Forwarded-For
chain, preventing IP spoofing to bypass rate limits.
What was already solid
The audit confirmed several areas where the existing architecture is strong:
- Zero SQL injection vectors β all queries use parameterized inputs
-
No atom exhaustion risk β no
String.to_atomon user input - OAuth 2.1 with PKCE β MCP authentication follows the spec
-
Database TLS β production uses
verify_peerwith certificate pinning - HMAC verification β webhook signatures use timing-safe comparison
- Worker idempotency β background jobs handle retries safely
- CSRF protection β enabled on all browser routes
Tools used
- Manual code review of authentication, authorization, and data paths
- Sobelow β Phoenix security scanner
- Credo β static analysis (strict mode)
- Dialyzer β type checking
-
mix deps.auditβ dependency vulnerability scanning
Whatβs next
- Third-party penetration testing
- SOC 2 Type II preparation
- Automated security regression tests for resolved findings