2025-11-09 11:42:52 +00:00
# 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
2025-11-12 16:04:43 +00:00
### 5. Rate Limiting & Origin Checking
2025-11-09 11:42:52 +00:00
2025-11-12 16:04:43 +00:00
**Status: ** ✅ **Implemented **
2025-11-09 11:42:52 +00:00
2025-11-12 16:04:43 +00:00
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.
2025-11-09 11:42:52 +00:00
### 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
2025-11-12 16:04:43 +00:00
## 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
```
### 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
2025-11-09 11:42:52 +00:00
### 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!