Files
cv-site/docs/CONTACT_FORM_IMPLEMENTATION.md
T
juanatsap 58c1237326 feat: Add secure contact form with comprehensive security features
- Add contact form dialog with HTMX integration (hx-post)
- Implement browser-only access middleware (blocks curl/Postman/wget)
- Add rate limiting (5 requests/hour per IP) for contact endpoint
- Implement honeypot and timing-based bot detection
- Add input validation (email format, message length 10-5000 chars)
- Create contact button in desktop and mobile navigation (last position)

Security features:
- Browser-only middleware validates User-Agent, Referer/Origin, HX-Request headers
- Honeypot field returns fake success to fool bots while logging spam
- Timing validation rejects forms submitted < 2 seconds
- All security events logged for monitoring

Documentation:
- docs/SECURITY.md - Comprehensive security documentation
- docs/HACK-CHALLENGE.md - "Try to Hack Me!" challenge for security researchers
- docs/SECURITY-AUDIT-REPORT.md - Full security audit report
- docs/CONTACT-FORM-QUICKSTART.md - Integration guide

Form fields: email (required), name, company, subject, message (required)
2025-11-30 14:31:58 +00:00

13 KiB

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

# 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:

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):

<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)

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)

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)

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

# 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

<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)

// 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

# 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

    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

  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