Secure Contact Form Endpoint

<?php
// contact.php — Secure contact form handler
// Supports both regular POST and AJAX JSON requests

header('Content-Type: application/json');

// === Configuration ===
$to_email = '[email protected]';
$site_name = 'Your Website';
$secret_key = 'your-csrf-secret';  // Change this!

// === CSRF Protection ===
session_start();
if (!isset($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// === Rate Limiting (session-based) ===
$rate_limit = 3; // Max submissions per hour
$rate_window = 3600; // 1 hour in seconds

if (!isset($_SESSION['submit_count'])) {
    $_SESSION['submit_count'] = 1;
    $_SESSION['submit_time'] = time();
} elseif (time() - $_SESSION['submit_time'] < $rate_window) {
    $_SESSION['submit_count']++;
    if ($_SESSION['submit_count'] > $rate_limit) {
        http_response_code(429);
        die(json_encode(['error' => 'Too many requests. Please try again later.']));
    }
} else {
    $_SESSION['submit_count'] = 1;
    $_SESSION['submit_time'] = time();
}

// === Parse Input ===
$input = $_SERVER['CONTENT_TYPE'] === 'application/json'
    ? json_decode(file_get_contents('php://input'), true)
    : $_POST;

// === Validate CSRF Token ===
if (!isset($input['csrf_token']) || $input['csrf_token'] !== $_SESSION['csrf_token']) {
    http_response_code(403);
    die(json_encode(['error' => 'Invalid session. Please refresh the page.']));
}

// === Validate Input Fields ===
$errors = [];

$name = trim($input['name'] ?? '');
if (strlen($name) < 2 || strlen($name) > 100) {
    $errors[] = 'Name must be between 2 and 100 characters.';
}

$email = filter_var($input['email'] ?? '', FILTER_VALIDATE_EMAIL);
if (!$email) {
    $errors[] = 'Please enter a valid email address.';
}

$message = trim($input['message'] ?? '');
if (strlen($message) < 10 || strlen($message) > 5000) {
    $errors[] = 'Message must be between 10 and 5000 characters.';
}

if (!empty($errors)) {
    http_response_code(422);
    die(json_encode(['error' => implode(' ', $errors)]));
}

// === Honeypot Check (anti-spam) ===
if (!empty($input['website'])) {
    // Bot filled hidden field
    http_response_code(200);
    die(json_encode(['success' => true, 'message' => 'Thank you!'])); // Silent reject
}

// === Send Email ===
$subject = sprintf('[%s] New inquiry from %s', $site_name, $name);
$body = "Name: $name\nEmail: $email\n\nMessage:\n$message";
$headers = "From: $name <$email>\r\nReply-To: $email\r\n";
$headers .= "X-Mailer: PHP/" . phpversion();

if (mail($to_email, $subject, $body, $headers)) {
    echo json_encode(['success' => true, 'message' => 'Thank you! We will get back to you soon.']);
} else {
    http_response_code(500);
    echo json_encode(['error' => 'Failed to send message. Please try again later.']);
}
?>

Frontend Form HTML

<form id="contactForm" method="POST" action="contact.php">
  <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
  <input type="hidden" name="website" style="display:none" tabindex="-1" autocomplete="off">

  <div>
    <label for="name">Name</label>
    <input type="text" name="name" id="name" required minlength="2">
  </div>
  <div>
    <label for="email">Email</label>
    <input type="email" name="email" id="email" required>
  </div>
  <div>
    <label for="message">Message</label>
    <textarea name="message" id="message" required minlength="10"></textarea>
  </div>

  <button type="submit">Send Message</button>
  <div id="formStatus"></div>
</form>

Security Features

  • ✅ CSRF token protection
  • ✅ Rate limiting (3 submissions/hour)
  • ✅ Input validation & sanitization
  • ✅ Honeypot anti-spam field
  • ✅ Prepared email headers (prevent injection)
  • ✅ JSON error responses for AJAX
  • ✅ Session-based rate limiting