-
Notifications
You must be signed in to change notification settings - Fork 0
Develop #152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Develop #152
Changes from all commits
da3ff55
315c1b5
d66133e
5086e93
4d9fcb5
dc2e2a1
11b3e94
e22a0ff
236f4d8
dbc8a49
8b0afc8
977d2f7
a50a681
19f6627
eed09c1
4c32216
a2ba4c2
5c5a8b0
51f4fae
604dfe6
37d3e2c
7f8500d
8f21ccc
6d43163
e13952c
3503a05
7c274a9
984c0e0
4c8e41a
0daca06
e99cb0b
b2cec57
72df31e
0be3ae5
c0212d0
1f9a729
d084d60
f0cd0a7
52b5256
81c93f2
970fed4
79db2a3
7c50fac
a45851d
6483dc4
ec7a93b
bca8391
9eba7a5
f261f9f
ee20df8
aca80c1
05c53f2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,164 @@ | ||||||||||||||
| # Anthropic API Proxy Security | ||||||||||||||
|
|
||||||||||||||
| ## Problem | ||||||||||||||
|
|
||||||||||||||
| When running Claude Agent SDK in Vercel Sandbox, the `ANTHROPIC_API_KEY` must be provided as an environment variable. This creates a security risk: malicious users could extract the API key from the sandbox environment. | ||||||||||||||
|
|
||||||||||||||
| ## Solution: Secure Proxy | ||||||||||||||
|
|
||||||||||||||
| Instead of exposing the API key directly, we route all Anthropic API calls through a secure proxy endpoint. | ||||||||||||||
|
|
||||||||||||||
| ### Architecture | ||||||||||||||
|
|
||||||||||||||
| ``` | ||||||||||||||
| ┌─────────────────────────────────────────────┐ | ||||||||||||||
| │ Vercel Sandbox (Untrusted) │ | ||||||||||||||
| │ │ | ||||||||||||||
| │ Claude Agent SDK │ | ||||||||||||||
| │ ├─ ANTHROPIC_BASE_URL = /api/anthropic... │ | ||||||||||||||
| │ └─ ANTHROPIC_API_KEY = "placeholder" │ | ||||||||||||||
| │ │ | ||||||||||||||
| │ [No real API key exposed] │ | ||||||||||||||
| └──────────────┬──────────────────────────────┘ | ||||||||||||||
| │ Authenticated requests | ||||||||||||||
| │ (user_id + secret in URL) | ||||||||||||||
| ▼ | ||||||||||||||
| ┌─────────────────────────────────────────────┐ | ||||||||||||||
| │ /api/anthropic-proxy (Trusted) │ | ||||||||||||||
| │ │ | ||||||||||||||
| │ 1. Verify internal auth secret │ | ||||||||||||||
| │ 2. Check rate limits per user │ | ||||||||||||||
| │ 3. Add real ANTHROPIC_API_KEY │ | ||||||||||||||
| │ 4. Forward to api.anthropic.com │ | ||||||||||||||
| │ 5. Return response │ | ||||||||||||||
| └─────────────────────────────────────────────┘ | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| ## Implementation | ||||||||||||||
|
|
||||||||||||||
| ### 1. Proxy Endpoint | ||||||||||||||
|
|
||||||||||||||
| **File:** `src/app/api/anthropic-proxy/route.ts` | ||||||||||||||
|
|
||||||||||||||
| - Accepts requests with `user_id` and `auth` query parameters | ||||||||||||||
| - Validates internal authentication secret | ||||||||||||||
| - Enforces per-user rate limiting (50 requests/hour by default) | ||||||||||||||
| - Adds the real `ANTHROPIC_API_KEY` before forwarding to Anthropic | ||||||||||||||
| - Supports streaming and non-streaming requests | ||||||||||||||
|
|
||||||||||||||
| ### 2. Sandbox Configuration | ||||||||||||||
|
|
||||||||||||||
| **File:** `src/lib/domains/agentic/repositories/claude-agent-sdk-sandbox.repository.ts` | ||||||||||||||
|
|
||||||||||||||
| ```typescript | ||||||||||||||
| // Set proxy URL with authentication | ||||||||||||||
| const proxyUrl = `${baseUrl}/api/anthropic-proxy?user_id=${userId}&auth=${secret}` | ||||||||||||||
| process.env.ANTHROPIC_BASE_URL = proxyUrl | ||||||||||||||
|
|
||||||||||||||
| // Placeholder key (never used) | ||||||||||||||
| process.env.ANTHROPIC_API_KEY = 'placeholder-key-not-used' | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| ## Security Features | ||||||||||||||
|
|
||||||||||||||
| ### 1. Internal Authentication | ||||||||||||||
| - Requests must include `INTERNAL_PROXY_SECRET` | ||||||||||||||
| - Secret is never exposed to sandbox (only in query string generated server-side) | ||||||||||||||
| - Generate a strong random secret for production | ||||||||||||||
|
|
||||||||||||||
| ### 2. Rate Limiting | ||||||||||||||
| - 50 requests/hour per user (configurable) | ||||||||||||||
| - Prevents abuse even if authentication is compromised | ||||||||||||||
| - In-memory tracking (use Redis for production) | ||||||||||||||
|
|
||||||||||||||
| ### 3. Budget Limits | ||||||||||||||
| - `maxBudgetUsd: 1.0` per SDK request | ||||||||||||||
| - Additional limit at SDK level | ||||||||||||||
| - Prevents runaway costs | ||||||||||||||
|
|
||||||||||||||
| ### 4. Request Validation | ||||||||||||||
| - Body validation before forwarding | ||||||||||||||
| - Suspicious pattern detection (optional) | ||||||||||||||
| - Request logging for monitoring | ||||||||||||||
|
|
||||||||||||||
| ## Environment Variables | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| # Required | ||||||||||||||
| ANTHROPIC_API_KEY=sk-ant-... # Real API key (server-side only) | ||||||||||||||
| INTERNAL_PROXY_SECRET=<random> # Generate with: openssl rand -hex 32 | ||||||||||||||
|
|
||||||||||||||
| # Optional | ||||||||||||||
| HEXFRAME_API_BASE_URL=https://... # Your app URL for proxy | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| ## Production Setup | ||||||||||||||
|
|
||||||||||||||
| 1. **Generate secure secret:** | ||||||||||||||
| ```bash | ||||||||||||||
| openssl rand -hex 32 | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| 2. **Add to Vercel:** | ||||||||||||||
| - Go to Project Settings → Environment Variables | ||||||||||||||
| - Add `INTERNAL_PROXY_SECRET` with the generated value | ||||||||||||||
| - Scope: Production, Preview, Development | ||||||||||||||
|
|
||||||||||||||
| 3. **Verify:** | ||||||||||||||
| - `ANTHROPIC_API_KEY` is set (for proxy to use) | ||||||||||||||
| - `INTERNAL_PROXY_SECRET` is set (for auth) | ||||||||||||||
| - `HEXFRAME_API_BASE_URL` points to your domain | ||||||||||||||
|
|
||||||||||||||
| ## Monitoring | ||||||||||||||
|
|
||||||||||||||
| Monitor for: | ||||||||||||||
| - Rate limit violations (potential abuse) | ||||||||||||||
| - Failed auth attempts | ||||||||||||||
| - Unusual API usage patterns | ||||||||||||||
| - High costs per user | ||||||||||||||
|
|
||||||||||||||
| Check logs: | ||||||||||||||
| ```typescript | ||||||||||||||
| loggers.agentic('Anthropic proxy: ...', { userId, ... }) | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| ## Limitations | ||||||||||||||
|
|
||||||||||||||
| ### What This Protects Against | ||||||||||||||
| ✅ Direct API key extraction from process.env | ||||||||||||||
| ✅ Unlimited API usage per user | ||||||||||||||
| ✅ Untracked API consumption | ||||||||||||||
|
|
||||||||||||||
| ### What This Doesn't Protect Against | ||||||||||||||
| ⚠️ Sophisticated attacks (sandbox escape, timing attacks) | ||||||||||||||
| ⚠️ Auth secret extraction (if sandbox can read its own request URLs) | ||||||||||||||
| ⚠️ Replay attacks (no nonce/timestamp validation) | ||||||||||||||
|
|
||||||||||||||
| ### Additional Hardening (Optional) | ||||||||||||||
|
|
||||||||||||||
| For maximum security: | ||||||||||||||
| 1. **Use time-based tokens:** Include timestamp in auth, reject old requests | ||||||||||||||
| 2. **Use per-request nonces:** Prevent replay attacks | ||||||||||||||
| 3. **Use Redis for rate limiting:** More robust than in-memory | ||||||||||||||
| 4. **Monitor sandbox logs:** Detect key extraction attempts | ||||||||||||||
| 5. **Rotate secrets regularly:** Weekly/monthly rotation | ||||||||||||||
|
|
||||||||||||||
| ## Cost Control | ||||||||||||||
|
|
||||||||||||||
| Even with leaked credentials, damage is limited by: | ||||||||||||||
| - **Rate limiting:** 50 req/hour = max ~$1-2/hour (at $0.02/req avg) | ||||||||||||||
| - **Budget limits:** $1.00 max per SDK request | ||||||||||||||
| - **Monitoring:** Alerts on unusual usage | ||||||||||||||
|
|
||||||||||||||
| ## Alternative: Don't Use Sandbox | ||||||||||||||
|
|
||||||||||||||
| If security concerns are too high: | ||||||||||||||
| - Set `LLM_PROVIDER=openrouter` in production | ||||||||||||||
| - Keep Claude SDK for development only | ||||||||||||||
| - Simpler but loses agent capabilities | ||||||||||||||
|
|
||||||||||||||
| ## References | ||||||||||||||
|
|
||||||||||||||
| - [Anthropic SDK Base URL](https://github.com/anthropics/anthropic-sdk-typescript) | ||||||||||||||
| - [Vercel Sandbox Docs](https://vercel.com/docs/vercel-sandbox) | ||||||||||||||
| - [Implementation PR](#) <!-- Add PR link when merged --> | ||||||||||||||
|
Comment on lines
+162
to
+164
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace the placeholder PR link. markdownlint (MD042) rejects the empty -- [Implementation PR](#) <!-- Add PR link when merged -->
+- [Implementation PR](https://github.com/Diplow/hexframe/pull/152)📝 Committable suggestion
Suggested change
🧰 Tools🪛 markdownlint-cli2 (0.18.1)164-164: No empty links (MD042, no-empty-links) 🤖 Prompt for AI Agents |
||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix Markdown fenced block language.
markdownlint is failing (MD040) because the architecture block lacks a language hint. Please declare it (e.g.
text) so docs builds stay green.🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
13-13: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents