# API Protection & Security **Protection against external access and DDoS attacks on resource-intensive endpoints.** --- ## ✅ VERIFICATION STATUS **Last Tested:** November 9, 2025 **Status:** ✅ **ALL PROTECTION MECHANISMS VERIFIED WORKING** ### Verified Test Results | Test | Expected | Actual | Status | |------|----------|--------|--------| | External referer (evil.com) | 403 Forbidden | 403 Forbidden | ✅ PASS | | Localhost referer | 200 OK | 200 OK | ✅ PASS | | Production domain referer | 200 OK | 200 OK | ✅ PASS | | External Origin header | 403 Forbidden | 403 Forbidden | ✅ PASS | | No referer (development) | 200 OK | 200 OK | ✅ PASS | | Rate limit (requests 1-3) | 200 OK | 200 OK | ✅ PASS | | Rate limit (request 4+) | 429 Too Many | 429 Too Many | ✅ PASS | **Protection Layers:** Origin checking + Rate limiting both working correctly. --- ## Overview The CV website implements multiple layers of protection to prevent external sites from accessing the API and to protect against DDoS attacks on resource-intensive endpoints like PDF generation. ### Protection Layers 1. **Origin Checking** - Only allows requests from your domain 2. **Rate Limiting** - Prevents abuse even from allowed origins 3. **Production Restrictions** - Stricter rules in production environments --- ## 1. Origin Checking **File:** `internal/middleware/security.go` (`OriginChecker` middleware) ### How It Works The origin checker examines incoming HTTP requests and validates them against a whitelist of allowed domains. It checks two headers: 1. **Origin Header** - Set by browsers for CORS requests 2. **Referer Header** - Set by browsers for navigation requests ### Configuration **Environment Variable:** `ALLOWED_ORIGINS` ```bash # Development (default) ALLOWED_ORIGINS= # Production ALLOWED_ORIGINS=yourdomain.com,www.yourdomain.com ``` ### Behavior | Environment | No Header | Localhost | Your Domain | External Domain | |-------------|-----------|-----------|-------------|-----------------| | **Development** | ✅ Allowed | ✅ Allowed | ✅ Allowed | ❌ Blocked | | **Production** | ❌ Blocked (PDF) | ✅ Allowed | ✅ Allowed | ❌ Blocked | **Production PDF Endpoint:** - Requires `Origin` or `Referer` header - Blocks direct URL access without headers - Prevents bookmarking and external hotlinking ### Example Responses **Allowed Request:** ```bash curl -H "Referer: https://yourdomain.com/" http://localhost:1999/export/pdf?lang=en # Status: 200 OK # PDF file downloaded ``` **Blocked Request (External Domain):** ```bash curl -H "Referer: https://externaldomain.com/" http://localhost:1999/export/pdf?lang=en # Status: 403 Forbidden # Response: Forbidden: External access not allowed ``` **Blocked Request (Production, No Headers):** ```bash # In production with GO_ENV=production curl http://yourdomain.com/export/pdf?lang=en # Status: 403 Forbidden # Response: Forbidden: Direct access not allowed ``` --- ## 2. Rate Limiting **File:** `internal/middleware/security.go` (`RateLimiter`) ### How It Works The rate limiter tracks requests per IP address and enforces limits on resource-intensive endpoints. **Current Configuration:** - **Limit:** 3 requests per minute per IP - **Window:** 1 minute - **Applied to:** `/export/pdf` endpoint only ### Implementation ```go // Create rate limiter for PDF endpoint pdfRateLimiter := middleware.NewRateLimiter(3, 1*time.Minute) // Apply to PDF endpoint protectedPDFHandler := middleware.OriginChecker( pdfRateLimiter.Middleware( http.HandlerFunc(cvHandler.ExportPDF), ), ) ``` ### Behavior | Requests | Status | Response | |----------|--------|----------| | 1st request | ✅ 200 OK | PDF generated | | 2nd request | ✅ 200 OK | PDF generated | | 3rd request | ✅ 200 OK | PDF generated | | 4th request (within 1 min) | ❌ 429 Too Many Requests | Rate limit exceeded | | After 1 minute | ✅ 200 OK | Counter reset | ### Headers **Rate Limit Exceeded Response:** ``` HTTP/1.1 429 Too Many Requests Retry-After: 60 Content-Type: text/plain; charset=utf-8 Rate limit exceeded. Please try again later. ``` ### IP Detection The rate limiter detects client IP from: 1. `X-Forwarded-For` header (proxy/CDN) 2. `X-Real-IP` header (alternative proxy header) 3. `RemoteAddr` (direct connection) **Supports reverse proxies:** Yes (Nginx, Cloudflare, etc.) --- ## 3. Combined Protection The PDF endpoint has **both** origin checking and rate limiting applied: ``` Request → OriginChecker → RateLimiter → PDF Handler ``` **Protection Flow:** 1. **Check Origin/Referer** - If external domain → 403 Forbidden - If production + no headers → 403 Forbidden - Otherwise, continue 2. **Check Rate Limit** - If > 3 requests/minute → 429 Too Many Requests - Otherwise, continue 3. **Generate PDF** - Process request normally --- ## 4. Configuration Examples ### Development Environment ```bash # .env PORT=1999 HOST=localhost GO_ENV=development ALLOWED_ORIGINS= ``` **Behavior:** - Allows `localhost` and `127.0.0.1` - Allows requests without headers - Rate limit: 3 PDF/min per IP ### Production Environment ```bash # .env PORT=1999 HOST=0.0.0.0 GO_ENV=production ALLOWED_ORIGINS=yourdomain.com,www.yourdomain.com ``` **Behavior:** - Only allows `yourdomain.com` and `www.yourdomain.com` - Requires `Origin` or `Referer` header for PDF endpoint - Rate limit: 3 PDF/min per IP ### Multiple Domains ```bash # Support multiple domains (e.g., staging + production) ALLOWED_ORIGINS=yourdomain.com,www.yourdomain.com,staging.yourdomain.com ``` --- ## 5. Testing Protection ### Test Origin Checking ```bash # ✅ Allowed (localhost in development) curl http://localhost:1999/export/pdf?lang=en # ✅ Allowed (with referer) curl -H "Referer: http://localhost:1999/" http://localhost:1999/export/pdf?lang=en # ❌ Blocked (external referer) curl -H "Referer: https://evil.com/" http://localhost:1999/export/pdf?lang=en # Expected: 403 Forbidden ``` ### Test Rate Limiting ```bash # Generate 4 PDFs quickly for i in {1..4}; do echo "Request $i:" curl -w "Status: %{http_code}\n" -o /dev/null -s http://localhost:1999/export/pdf?lang=en sleep 1 done # Expected output: # Request 1: Status: 200 # Request 2: Status: 200 # Request 3: Status: 200 # Request 4: Status: 429 ``` ### Test Combined Protection ```bash # Should be blocked by origin checker before rate limiter for i in {1..5}; do curl -H "Referer: https://evil.com/" -w "Status: %{http_code}\n" -o /dev/null -s \ http://localhost:1999/export/pdf?lang=en done # Expected: All requests get 403 (origin check fails immediately) ``` --- ## 6. Monitoring & Logs ### Log Messages **Origin Check Failure:** ``` # No specific log (returns 403 silently) # Check server logs for 403 responses ``` **Rate Limit Exceeded:** ``` # No specific log (returns 429 silently) # Monitor for frequent 429 responses ``` ### Recommended Monitoring 1. **Track 403 responses** - Indicates potential attack attempts 2. **Track 429 responses** - Indicates rate limiting in effect 3. **Monitor PDF generation times** - Detect abuse patterns 4. **Alert on sustained high request rates** - DDoS detection --- ## 7. Customization ### Adjust Rate Limits **File:** `main.go` ```go // Current: 3 requests per minute pdfRateLimiter := middleware.NewRateLimiter(3, 1*time.Minute) // More restrictive: 5 per hour pdfRateLimiter := middleware.NewRateLimiter(5, 1*time.Hour) // Less restrictive: 10 per minute pdfRateLimiter := middleware.NewRateLimiter(10, 1*time.Minute) ``` ### Apply to Other Endpoints ```go // Protect /cv endpoint protectedCVHandler := middleware.OriginChecker( http.HandlerFunc(cvHandler.CVContent), ) mux.Handle("/cv", protectedCVHandler) ``` ### Disable Origin Checking (Not Recommended) ```go // Apply only rate limiting (no origin check) mux.Handle("/export/pdf", pdfRateLimiter.Middleware( http.HandlerFunc(cvHandler.ExportPDF), )) ``` --- ## 8. Security Best Practices ### ✅ Recommended 1. **Set ALLOWED_ORIGINS in production** - Never run production without it 2. **Use HTTPS** - Prevents header spoofing 3. **Monitor 403/429 responses** - Detect attack patterns 4. **Consider CloudFlare** - Additional DDoS protection layer 5. **Log suspicious requests** - For forensic analysis ### ❌ Anti-Patterns 1. **Don't disable protection in production** - Always use origin checking 2. **Don't set rate limits too high** - PDF generation is expensive 3. **Don't trust IP addresses alone** - Use combined protection 4. **Don't expose internal endpoints** - Keep admin routes private --- ## 9. Production Deployment Checklist Before deploying to production: - [ ] Set `GO_ENV=production` in environment - [ ] Configure `ALLOWED_ORIGINS` with your domain(s) - [ ] Test origin checking with external domain - [ ] Test rate limiting with rapid requests - [ ] Verify HTTPS is enabled (prevents header spoofing) - [ ] Set up monitoring for 403/429 responses - [ ] Configure log retention for security analysis - [ ] Test PDF generation under load - [ ] Verify reverse proxy headers (X-Forwarded-For) - [ ] Document allowed origins in runbook --- ## 10. Troubleshooting ### Problem: Legitimate users getting 403 **Cause:** ALLOWED_ORIGINS not configured correctly **Solution:** ```bash # Ensure all your domains are listed ALLOWED_ORIGINS=yourdomain.com,www.yourdomain.com # Check for typos (case-insensitive but must match exactly) ``` ### Problem: Rate limit too restrictive **Cause:** Legitimate users hitting limit **Solution:** ```go // Increase limit or window in main.go pdfRateLimiter := middleware.NewRateLimiter(5, 1*time.Minute) ``` ### Problem: Behind reverse proxy, rate limit not working **Cause:** IP detection failing **Solution:** ```nginx # Ensure Nginx passes correct headers proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; ``` ### Problem: Origin header not being sent **Cause:** Browser doesn't send Origin for same-origin requests **Solution:** This is normal. The middleware checks Referer as fallback. --- ## 11. Attack Scenarios & Mitigation ### Scenario 1: DDoS via PDF Generation **Attack:** External site hotlinks to `/export/pdf`, triggering many PDF generations **Mitigation:** 1. ✅ Origin checker blocks external domains (403) 2. ✅ Rate limiter prevents >3 requests/min per IP (429) 3. ✅ Production mode requires headers (blocks direct access) **Result:** Attack fails, server protected ### Scenario 2: Header Spoofing **Attack:** Attacker spoofs `Referer` header to bypass origin check **Mitigation:** 1. ⚠️ HTTPS prevents header modification in transit 2. ✅ Rate limiter still applies (3 req/min limit) 3. ✅ IP-based tracking prevents distributed spoofing **Result:** Individual attacker limited to 3 req/min ### Scenario 3: Distributed Attack **Attack:** Botnet with many IPs, each generating PDFs **Mitigation:** 1. ✅ Each IP limited to 3 req/min 2. ✅ Origin checker blocks if no valid referer 3. 🔴 Consider CloudFlare for large-scale DDoS **Result:** Slowed but not fully blocked (add CloudFlare) --- ## Summary **Protection Enabled:** ✅ Origin Checking + Rate Limiting **Endpoints Protected:** - `/export/pdf` - Full protection (origin + rate limit) **Endpoints Unprotected:** - `/` - Public home page - `/cv` - Public CV content - `/health` - Public health check - `/static/*` - Public static files **Configuration:** Environment-based via `ALLOWED_ORIGINS` **Production Ready:** Yes (after setting ALLOWED_ORIGINS) --- **For questions or to adjust protection levels, modify:** - `internal/middleware/security.go` - Origin checking and rate limiting logic - `main.go` - Apply protection to additional endpoints - `.env` - Configure ALLOWED_ORIGINS for your domain