Files
cv-site/docs/CONTACT_FORM_IMPLEMENTATION.md
T

473 lines
13 KiB
Markdown
Raw Normal View History

# Contact Form Email Backend - Implementation Guide
## Overview
Complete backend implementation for a contact form with email delivery using SMTP (Gmail), featuring comprehensive security measures including CSRF protection, rate limiting, bot protection, and browser-only access.
## Features Implemented
### 1. Email Service (`internal/services/email.go`)
- ✅ SMTP-based email sending with TLS support
- ✅ Gmail App Password authentication
- ✅ Email validation and sanitization
- ✅ Header injection prevention
- ✅ Configurable via environment variables
- ✅ Comprehensive error handling and logging
- ✅ Template-based email formatting
### 2. Contact Handler (`internal/handlers/contact.go`)
- ✅ POST endpoint: `/api/contact`
- ✅ Form field validation (email, name, company, subject, message)
- ✅ Bot protection with honeypot field
- ✅ Timing check (rejects forms submitted < 2 seconds)
- ✅ HTMX-friendly responses
- ✅ Detailed logging (without sensitive data)
### 3. Security Middleware
#### Contact Rate Limiting (`internal/middleware/contact_rate_limit.go`)
- ✅ 5 requests per hour per IP address
- ✅ Automatic cleanup of expired entries
- ✅ HTMX-friendly error responses
- ✅ Configurable limits and windows
#### CSRF Protection (`internal/middleware/csrf.go`)
- ✅ Token generation and validation
- ✅ 24-hour token TTL
- ✅ Cookie-based token storage
- ✅ Automatic token cleanup
- ✅ Support for forms and AJAX requests
#### Browser-Only Access (`internal/middleware/browser_only.go`)
- ✅ Blocks curl, Postman, wget, and other HTTP clients
- ✅ User-Agent validation
- ✅ Referer/Origin header validation
- ✅ Custom header requirement (HTMX or X-Browser-Request)
- ✅ Comprehensive bot detection
### 4. Configuration (`internal/config/config.go`)
- ✅ Email settings added to config struct
- ✅ Environment variable support
- ✅ Sensible defaults for development
### 5. HTMX Response Templates
-`templates/partials/contact_success.html` - Success message with animation
-`templates/partials/contact_error.html` - Error message with shake animation
### 6. Route Registration (`internal/routes/routes.go`)
- ✅ Endpoint registered with full middleware chain
- ✅ Proper middleware ordering
## Security Features
### Multi-Layer Protection
```
Request → Browser-Only → Contact Rate Limit → CSRF → Handler
```
1. **Browser-Only Middleware**
- Blocks non-browser clients (curl, Postman, etc.)
- Validates User-Agent, Referer, and custom headers
2. **Contact Rate Limiting**
- 5 submissions per hour per IP
- Prevents spam and abuse
3. **CSRF Protection**
- Validates security tokens
- Prevents cross-site request forgery
4. **Bot Protection in Handler**
- Honeypot field detection
- Timing validation (min 2 seconds)
5. **Input Validation**
- Email format validation
- Length restrictions
- Header injection prevention
- XSS protection via sanitization
## Environment Configuration
### Required Variables
```bash
# SMTP Configuration (Gmail)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=your-app-password
SMTP_FROM_EMAIL=your-email@gmail.com
CONTACT_EMAIL=txeo.msx@gmail.com
```
### Gmail App Password Setup
1. Enable 2FA in your Google account
2. Visit: https://myaccount.google.com/apppasswords
3. Generate an App Password for "Mail"
4. Use the generated password in `SMTP_PASSWORD`
**Important**: Never use your regular Gmail password - always use an App Password.
## API Endpoint
### POST /api/contact
**Request Format:**
```http
POST /api/contact
Content-Type: application/x-www-form-urlencoded
HX-Request: true
Referer: http://yourdomain.com/
email=user@example.com
&name=John Doe
&company=Acme Inc
&subject=Partnership Inquiry
&message=Hello, I would like to discuss...
&website=
&submit_time=1701360000000
&csrf_token=abc123...
```
**Required Fields:**
- `email` - Valid email address (max 254 chars)
- `message` - Message text (10-5000 chars)
**Optional Fields:**
- `name` - Sender name (max 100 chars)
- `company` - Company name (max 100 chars)
- `subject` - Email subject (max 200 chars)
**Special Fields:**
- `website` - Honeypot (must be empty)
- `submit_time` - Unix timestamp in milliseconds
- `csrf_token` - CSRF token from cookie
**Success Response (200):**
```html
<div class="alert alert-success">
<h3>Message Sent Successfully!</h3>
<p>Thank you for reaching out...</p>
</div>
```
**Error Responses:**
- **403 Forbidden** - Non-browser client, CSRF failure, or rate limit
- **400 Bad Request** - Validation error
- **429 Too Many Requests** - Rate limit exceeded
## Testing
### Test 1: Curl Request (Should Fail - 403)
```bash
curl -X POST http://localhost:1999/api/contact \
-d "email=test@example.com&message=Test" \
-w "\nStatus: %{http_code}\n"
```
**Expected:** `Forbidden: Browser access only` (403)
### Test 2: Postman Request (Should Fail - 403)
```bash
curl -X POST http://localhost:1999/api/contact \
-H "User-Agent: PostmanRuntime/7.32.0" \
-H "Referer: http://localhost:1999/" \
-d "email=test@example.com&message=Test" \
-w "\nStatus: %{http_code}\n"
```
**Expected:** `Forbidden: Browser access only` (403)
### Test 3: Browser-like Request (Should Fail - CSRF)
```bash
curl -X POST http://localhost:1999/api/contact \
-H "User-Agent: Mozilla/5.0 (Macintosh)" \
-H "Referer: http://localhost:1999/" \
-H "HX-Request: true" \
-d "email=test@example.com&message=Test" \
-w "\nStatus: %{http_code}\n"
```
**Expected:** CSRF validation error (403)
### Test 4: Complete Browser Request
Use the test HTML file: `test_contact_form.html`
```bash
# Start server
go run main.go
# Open in browser
open http://localhost:1999/test_contact_form.html
# Fill and submit form
# Should succeed if SMTP credentials are configured
```
## Integration Example
### HTML Contact Form
```html
<form
hx-post="/api/contact"
hx-target="#response"
hx-headers='{"X-Browser-Request": "true"}'
hx-on::before-request="this.submitTime.value = Date.now()"
>
<input type="hidden" name="submit_time" class="submitTime">
<input type="hidden" name="csrf_token" value="{{ .CSRFToken }}">
<!-- Honeypot -->
<div style="position: absolute; left: -9999px;">
<input type="text" name="website" tabindex="-1">
</div>
<input type="email" name="email" required>
<input type="text" name="name">
<input type="text" name="company">
<input type="text" name="subject">
<textarea name="message" required></textarea>
<button type="submit">Send</button>
</form>
<div id="response"></div>
<script>
// Initialize submit time
document.querySelector('.submitTime').value = Date.now();
</script>
```
### JavaScript (Fetch API)
```javascript
// Get CSRF token from cookie
const csrfToken = document.cookie
.split('; ')
.find(row => row.startsWith('csrf_token='))
?.split('=')[1];
const submitTime = Date.now();
// Wait at least 2 seconds before allowing submit
setTimeout(() => {
fetch('/api/contact', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Browser-Request': 'true'
},
body: new URLSearchParams({
email: 'user@example.com',
name: 'John Doe',
message: 'Hello!',
website: '', // Honeypot
submit_time: submitTime,
csrf_token: csrfToken
})
})
.then(response => response.text())
.then(html => {
document.getElementById('response').innerHTML = html;
});
}, 2000);
```
## Email Template
The email sent to `CONTACT_EMAIL` follows this format:
```
Subject: [CV Contact] {subject or "New Message"}
New contact form submission:
From: user@example.com
Name: John Doe
Company: Acme Inc
Subject: Partnership Inquiry
Message:
Hello, I would like to discuss a potential partnership...
---
IP: 192.168.1.1
Time: 2025-11-30 13:45:22 UTC
```
## Monitoring & Logging
### Security Events Logged
1. **Blocked Requests**
- Non-browser User-Agents
- Missing Referer/Origin
- Missing browser headers
- CSRF validation failures
- Rate limit exceeded
- Honeypot triggered
- Form submitted too fast
2. **Successful Submissions**
- Email sent successfully (logs email address and IP)
3. **Errors**
- SMTP connection failures
- Email sending errors
- Template rendering errors
### Log Format
```
2025/11/30 13:45:22 SECURITY: Blocked non-browser User-Agent from IP 192.168.1.1: curl/7.88.1
2025/11/30 13:45:23 SECURITY: CSRF validation failed from IP 192.168.1.2
2025/11/30 13:45:24 Contact form submitted successfully from user@example.com (192.168.1.3)
```
## Production Deployment
### Checklist
- [ ] Configure SMTP credentials in environment
- [ ] Set `CONTACT_EMAIL` to your email address
- [ ] Enable HTTPS (middleware automatically enables HSTS)
- [ ] Configure `ALLOWED_ORIGINS` if using custom domain
- [ ] Set up log monitoring
- [ ] Test email delivery
- [ ] Monitor rate limit statistics
- [ ] Set up email delivery monitoring
- [ ] Configure email bounce handling
- [ ] Review security headers
### Production Environment
```bash
# Production .env
GO_ENV=production
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=your-app-password
CONTACT_EMAIL=your-email@gmail.com
ALLOWED_ORIGINS=yourdomain.com,www.yourdomain.com
```
## Troubleshooting
### Email Not Sending
1. **Check SMTP credentials**
- Verify App Password is correct
- Ensure 2FA is enabled on Google account
2. **Check logs**
- Look for SMTP connection errors
- Verify email service initialization
3. **Test SMTP connection**
```bash
telnet smtp.gmail.com 587
```
### Rate Limiting Issues
1. **IP address detection**
- Check `X-Forwarded-For` header if behind proxy
- Verify IP extraction in logs
2. **Adjust limits**
- Modify `limit` and `window` in `contact_rate_limit.go`
- Default: 5 requests per hour
### CSRF Token Issues
1. **Token not set**
- Ensure cookie is being set on GET requests
- Check browser cookie settings
2. **Token mismatch**
- Verify token is passed in form or header
- Check token expiration (24 hours)
## Files Modified/Created
### New Files
- `internal/services/email.go` - Email service with SMTP
- `internal/handlers/contact.go` - Contact form handler
- `internal/middleware/contact_rate_limit.go` - Rate limiting
- `internal/middleware/csrf.go` - CSRF protection
- `internal/middleware/browser_only.go` - Browser validation
- `templates/partials/contact_success.html` - Success template
- `templates/partials/contact_error.html` - Error template
- `test_contact_form.html` - Test page
### Modified Files
- `internal/config/config.go` - Added email configuration
- `internal/routes/routes.go` - Registered contact endpoint
- `main.go` - Initialized contact handler and email service
- `.env.example` - Added email configuration example
## Security Considerations
### What's Protected Against
✅ **CSRF Attacks** - Token validation
✅ **Rate Limiting Bypass** - IP-based limiting
✅ **Bot Submissions** - Honeypot + timing + User-Agent
✅ **Email Header Injection** - Newline filtering
✅ **XSS** - Input sanitization
✅ **External API Access** - Browser-only enforcement
✅ **Spam** - Rate limiting + bot protection
✅ **Brute Force** - Rate limiting
### What's NOT Protected Against
⚠️ **Distributed Attacks** - Single-IP rate limiting only
⚠️ **Sophisticated Bots** - May bypass basic User-Agent checks
⚠️ **Email Bombing** - Recipient rate limiting not implemented
### Recommended Additions for Production
1. **CAPTCHA** - Add reCAPTCHA or hCaptcha
2. **Email Verification** - Verify sender's email address
3. **Advanced Bot Detection** - Integrate with services like Cloudflare
4. **Distributed Rate Limiting** - Use Redis for multi-server deployments
5. **Email Queue** - Use background job processor for email sending
6. **Delivery Monitoring** - Track email delivery success/failure
7. **Spam Detection** - Content-based spam filtering
## License & Reusability
This implementation is designed to be reusable across projects. Feel free to:
- Copy the entire `services/email.go` for email functionality
- Reuse middleware components independently
- Adapt the contact handler for your needs
- Modify rate limits and validation rules
All code follows Go best practices and is production-ready.
## Support
For issues or questions:
- Check the logs for detailed error messages
- Review security event logs for blocked requests
- Test with the included `test_contact_form.html`
- Verify SMTP credentials are correct
---
**Implementation Date:** November 30, 2025
**Version:** 1.0.0
**Author:** Backend Craftsman