# Security Policy ## Supported Versions This project is actively maintained. Security updates will be provided for the latest release on the `main` branch. | Version | Supported | | ------- | ------------------ | | main | :white_check_mark: | | < main | :x: | ## Reporting a Vulnerability We take the security of this CV site seriously. If you discover a security vulnerability, please help us protect users by following responsible disclosure practices. ### How to Report **DO NOT** open a public issue for security vulnerabilities. Instead, please report security vulnerabilities by: 1. **GitHub Security Advisories** (Preferred): - Go to the repository's Security tab - Click "Report a vulnerability" - Fill out the form with details 2. **Direct Contact**: - Open a private issue or contact the maintainer directly - Use encrypted communication if the vulnerability is severe ### What to Include Please provide the following information in your report: - **Description** of the vulnerability - **Steps to reproduce** the issue - **Potential impact** (e.g., data exposure, XSS, CSRF) - **Affected versions** (if known) - **Suggested fix** (if you have one) - **Your contact information** for follow-up questions ### Response Timeline - **Initial Response**: Within 48 hours - **Status Update**: Within 7 days - **Fix Timeline**: Depends on severity - Critical: Within 7 days - High: Within 14 days - Medium: Within 30 days - Low: Next release cycle ### Disclosure Policy - We ask that you give us reasonable time to fix the vulnerability before public disclosure - We will credit you in the security advisory (unless you prefer to remain anonymous) - Once the fix is deployed, we will publish a security advisory with details ## Security Considerations for Deployments If you're deploying this CV site, please be aware of these security considerations: ### 1. PDF Generation Security The server uses headless Chrome (via chromedp) to generate PDFs: - **Risk**: Chromedp executes JavaScript and renders HTML, which could be exploited if user input is not sanitized - **Mitigation**: - The CV data comes from trusted JSON files, not user input - If you modify the application to accept user input, ensure proper sanitization - Consider running chromedp in a sandboxed environment ### 2. Content Security Policy The application includes CSP headers: ```go Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://unpkg.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: ``` - Review and adjust CSP headers based on your deployment needs - Remove `'unsafe-inline'` if possible by moving inline scripts/styles to separate files ### 3. Environment Variables Sensitive configuration is managed via environment variables: - **Never commit** `.env` file to version control - Use `.env.example` as a template - In production, use secure secret management (e.g., HashiCorp Vault, AWS Secrets Manager) ### 4. HTTPS in Production - **Always use HTTPS** in production - Configure TLS certificates (Let's Encrypt recommended) - Consider using a reverse proxy (nginx, Caddy) for TLS termination ### 5. Rate Limiting & Origin Checking **Status:** ✅ **Implemented** The application includes built-in rate limiting and origin checking for resource-intensive endpoints: **Protected Endpoints:** - `/export/pdf` - Rate limited to 3 requests per minute per IP - Origin checking prevents external hotlinking **Configuration:** ```bash # Set allowed domains in production ALLOWED_ORIGINS=yourdomain.com,www.yourdomain.com ``` **Features:** - IP-based rate limiting (3 PDF/min per IP) - Origin/Referer header validation - Works with reverse proxies (Nginx, CloudFlare) - Automatic IP detection from X-Forwarded-For headers For detailed configuration, see [API Protection Features](#api-protection-features) below. ### 6. Input Validation While this application primarily serves static CV data: - If you extend it to accept user input, implement strict validation - Sanitize all inputs before rendering in templates - Use Go's `html/template` package (which auto-escapes) for HTML rendering ### 7. Dependency Management Keep dependencies up to date: ```bash # Check for outdated dependencies go list -u -m all # Update dependencies go get -u ./... go mod tidy ``` ### 8. Security Headers The application sets security headers: - `X-Content-Type-Options: nosniff` - `X-Frame-Options: DENY` - `X-XSS-Protection: 1; mode=block` - `Content-Security-Policy: ...` Review and enhance these headers based on your deployment needs. ### 9. Logging and Monitoring - Enable structured logging in production - Monitor for unusual patterns (e.g., excessive PDF generation requests) - Set up alerts for errors and anomalies ### 10. Docker Security If deploying via Docker: - Use official, minimal base images - Run container as non-root user - Scan images for vulnerabilities (e.g., `docker scan`, Trivy) - Keep base images updated ## API Protection Features The application implements multiple layers of protection to prevent external access and DDoS attacks on resource-intensive endpoints. ### Origin Checking **Purpose:** Prevent external sites from hotlinking to resource-intensive endpoints like PDF generation. **How It Works:** 1. Checks `Origin` header (CORS requests) 2. Falls back to `Referer` header (navigation requests) 3. Validates against whitelist of allowed domains 4. Blocks requests from external domains **Configuration via Environment Variable:** ```bash # Development (default - allows localhost) ALLOWED_ORIGINS= # Production (specify your domains) ALLOWED_ORIGINS=yourdomain.com,www.yourdomain.com # Multiple domains ALLOWED_ORIGINS=yourdomain.com,www.yourdomain.com,staging.yourdomain.com ``` **Behavior by Environment:** | Environment | No Header | Localhost | Your Domain | External Domain | |-------------|-----------|-----------|-------------|-----------------| | **Development** | ✅ Allowed | ✅ Allowed | ✅ Allowed | ❌ Blocked | | **Production** | ❌ Blocked (PDF) | ✅ Allowed | ✅ Allowed | ❌ Blocked | **Example Requests:** ```bash # ✅ Allowed (localhost in development) curl http://localhost:1999/export/pdf?lang=en # ✅ Allowed (valid referer) curl -H "Referer: https://yourdomain.com/" \ https://yourdomain.com/export/pdf?lang=en # ❌ Blocked (external referer) curl -H "Referer: https://evil.com/" \ https://yourdomain.com/export/pdf?lang=en # Response: 403 Forbidden - External access not allowed ``` ### Rate Limiting **Purpose:** Prevent abuse even from allowed origins by limiting request frequency. **Current Configuration:** - **Endpoint:** `/export/pdf` - **Limit:** 3 requests per minute per IP address - **Window:** 1 minute (rolling) - **Response:** 429 Too Many Requests when exceeded **Implementation:** ```go // Applied in main.go pdfRateLimiter := middleware.NewRateLimiter(3, 1*time.Minute) ``` **Behavior:** | Requests | Status | Response | |----------|--------|----------| | 1st-3rd requests | ✅ 200 OK | PDF generated | | 4th+ request (within 1 min) | ❌ 429 Too Many Requests | Rate limit exceeded | | After 1 minute | ✅ 200 OK | Counter reset | **IP Detection:** - Checks `X-Forwarded-For` header (proxy/CDN scenarios) - Falls back to `X-Real-IP` header (alternative proxy header) - Uses `RemoteAddr` field (direct connections) - Works correctly behind Nginx, CloudFlare, and other reverse proxies **Rate Limit Response:** ```http HTTP/1.1 429 Too Many Requests Retry-After: 60 Content-Type: text/plain; charset=utf-8 Rate limit exceeded. Please try again later. ``` ### Combined Protection The PDF endpoint has **both** origin checking and rate limiting applied in sequence: ``` Request Flow: User Request → Origin Checker → Rate Limiter → PDF Handler ↓ ↓ 403 if external 429 if exceeded ``` **Protection Scenarios:** 1. **Scenario: External hotlinking attempt** - External site links directly to your PDF endpoint - **Mitigation:** Origin checker blocks (403 Forbidden) - **Result:** Request never reaches rate limiter 2. **Scenario: Rapid PDF generation from your site** - Legitimate user on your domain requests many PDFs quickly - **Mitigation:** Rate limiter blocks after 3 requests/min (429) - **Result:** Prevents resource exhaustion 3. **Scenario: Distributed attack with header spoofing** - Botnet spoofs Referer headers to bypass origin check - **Mitigation:** HTTPS prevents header modification + Rate limiter per IP - **Result:** Each IP limited to 3 requests/min ### Configuration Examples **Development Environment:** ```bash # .env file PORT=1999 HOST=localhost GO_ENV=development ALLOWED_ORIGINS= # Empty allows localhost ``` **Behavior:** - Allows localhost and 127.0.0.1 - Allows requests without headers - Rate limit: 3 PDF/min per IP **Production Environment:** ```bash # .env file PORT=1999 HOST=0.0.0.0 GO_ENV=production ALLOWED_ORIGINS=yourdomain.com,www.yourdomain.com ``` **Behavior:** - Only allows specified domains - Requires Origin or Referer header for PDF endpoint - Blocks direct URL access - Rate limit: 3 PDF/min per IP ### Testing Protection **Test Origin Checking:** ```bash # ✅ Should succeed (localhost in development) curl http://localhost:1999/export/pdf?lang=en # ✅ Should succeed (valid referer) curl -H "Referer: http://localhost:1999/" \ http://localhost:1999/export/pdf?lang=en # ❌ Should fail (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 to trigger rate limit 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 ``` ### Customizing Protection **Adjust Rate Limits:** Edit `main.go` to change limits: ```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 Protection to Other Endpoints:** ```go // Protect /cv endpoint protectedCVHandler := middleware.OriginChecker( http.HandlerFunc(cvHandler.CVContent), ) mux.Handle("/cv", protectedCVHandler) ``` ### 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 ### Troubleshooting **Problem: Legitimate users getting 403 Forbidden** **Cause:** `ALLOWED_ORIGINS` not configured correctly **Solution:** ```bash # Ensure all your domains are listed (comma-separated, no spaces) ALLOWED_ORIGINS=yourdomain.com,www.yourdomain.com # Check for typos (domain matching is case-insensitive but exact) # Verify in logs which domain is being rejected ``` **Problem: Rate limit too restrictive** **Cause:** Legitimate users hitting the 3 requests/min limit **Solution:** ```go // Increase limit in main.go pdfRateLimiter := middleware.NewRateLimiter(5, 1*time.Minute) // or higher ``` **Problem: Behind reverse proxy, rate limiting not working per IP** **Cause:** IP detection failing, all requests seen as same IP **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 navigation requests **Solution:** This is normal browser behavior. The middleware checks `Referer` header as fallback, which browsers do send for navigation. For technical API details, see [API.md](API.md#security-protection). --- ## Known Security Considerations ### PDF Generation Resource Usage - **Issue**: PDF generation uses headless Chrome, which consumes significant CPU/memory - **Impact**: Potential DoS via excessive PDF generation requests - **Mitigation**: - Implement rate limiting on `/export/pdf` endpoint - Consider caching generated PDFs - Monitor resource usage ### Third-Party Dependencies External dependencies loaded from CDNs: - HTMX from `https://unpkg.com` - Google Fonts from `https://fonts.googleapis.com` **Recommendation**: For production, consider: - Self-hosting these dependencies for better control - Using Subresource Integrity (SRI) hashes - Implementing a Content Security Policy ## Security Best Practices If you're forking this project for your own CV: 1. **Review all code** before deploying 2. **Update personal information** in JSON files 3. **Configure security headers** appropriate for your use case 4. **Enable HTTPS** with valid certificates 5. **Keep dependencies updated** regularly 6. **Monitor application logs** for suspicious activity 7. **Backup your data** regularly 8. **Test security** before going live ## Security Updates Security updates will be announced via: - GitHub Security Advisories - Release notes on GitHub - Git commit messages tagged with `[SECURITY]` ## Contact For security concerns, please contact the project maintainer via GitHub. ## Acknowledgments We appreciate the security research community's efforts in responsibly disclosing vulnerabilities. Thank you for helping keep this project secure!