Files
anyllm-proxy/docs/security-audit.md
T
whit3rabbitandClaude Opus 4.6 f2e3ed15f4 Initial commit: Anthropic-to-OpenAI API translation proxy
Rust workspace with two crates:
- translator: pure, IO-free mapping between Anthropic Messages API and OpenAI Chat Completions
- proxy: axum HTTP server with auth, streaming SSE, retry/backoff, concurrency limits

169 tests passing (unit, golden fixture, integration).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 13:25:43 -05:00

2.9 KiB

Security Audit

Authentication

Proxy Auth Boundary

  • The proxy validates that requests carry either x-api-key or Authorization: Bearer header
  • The proxy does NOT verify the key value itself; it prevents accidental open proxying
  • Client credentials are never forwarded to OpenAI
  • OpenAI is always called with the server-configured OPENAI_API_KEY

Recommendation

  • For production, add key verification against an allowlist
  • Consider mTLS or OAuth/JWT at the proxy edge for multi-tenant deployments

Request Size Limits

  • 32 MB body limit enforced via axum's DefaultBodyLimit
  • Matches Anthropic's documented Messages endpoint limit
  • Prevents memory exhaustion from oversized payloads

SSRF Prevention

  • The proxy only makes outbound HTTP requests to OPENAI_BASE_URL (configured via env)
  • No user-controlled URLs are fetched:
    • Image URLs in content are passed as text, not fetched
    • Document blocks are converted to text notes, not processed
    • No file download/upload proxying

Recommendation

  • Validate OPENAI_BASE_URL is not a private/internal address at startup
  • Consider URL allowlist for production

Secret Handling

  • OPENAI_API_KEY is read from environment (never hardcoded)
  • redact_secret() utility available for logging (shows first/last 4 chars only)
  • Authorization headers are not logged
  • Request bodies may contain sensitive content; RUST_LOG should be info in production

Concurrency Protection

  • Tower ConcurrencyLimitLayer caps at 100 concurrent requests
  • Prevents self-DOS during upstream 429 incidents
  • Retry logic has exponential backoff to avoid hammering upstream

Header Filtering

  • Inbound x-api-key and Authorization headers are consumed, not forwarded
  • anthropic-version header is accepted but not forwarded
  • Outbound requests only include Authorization: Bearer with server key
  • x-request-id is generated/echoed for correlation

Streaming Security

  • Bounded channel (capacity 32) prevents unbounded memory growth
  • Client disconnect detected and upstream connection dropped
  • No content buffering; deltas translated directly

Dependencies

  • All dependencies are from crates.io (auditable)
  • No unsafe code in project source
  • cargo audit should be run in CI

OWASP Top 10 Relevance

Risk Status Notes
Injection Mitigated No SQL/shell; JSON parsed via serde
Broken Auth Partial Auth presence checked; value not verified
Sensitive Data Exposure Mitigated Secrets redacted in logs; env-based config
XML External Entities N/A JSON only
Broken Access Control N/A Single-purpose proxy
Security Misconfiguration Mitigated Sensible defaults; env-based config
XSS N/A API-only, no HTML
Insecure Deserialization Mitigated Typed serde with strict schemas
Using Components with Known Vulns Recommendation Run cargo audit in CI
Insufficient Logging Mitigated Structured tracing with request IDs