Files
ukaiautomation/contact-handler.php

482 lines
19 KiB
PHP
Raw Normal View History

2025-06-08 11:21:30 +01:00
<?php
// Enhanced Contact Form Handler with Security
ini_set('session.cookie_samesite', 'Lax');
ini_set('session.cookie_httponly', '1');
ini_set('session.cookie_secure', '1');
2025-06-08 11:21:30 +01:00
session_start();
// Form handler restored - temporary fix removed
2025-06-17 19:22:58 +01:00
// Include reCAPTCHA config
require_once '.recaptcha-config.php';
2025-06-08 11:21:30 +01:00
// Security headers
header('Content-Type: application/json');
// CSRF Protection
function generateCSRFToken() {
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
function validateCSRFToken($token) {
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}
// Rate limiting (simple implementation)
function checkRateLimit() {
$ip = $_SERVER['REMOTE_ADDR'];
$key = 'contact_' . md5($ip);
if (!isset($_SESSION[$key])) {
$_SESSION[$key] = ['count' => 0, 'time' => time()];
}
$data = $_SESSION[$key];
// Reset counter if more than 1 hour has passed
if (time() - $data['time'] > 3600) {
$_SESSION[$key] = ['count' => 0, 'time' => time()];
$data = $_SESSION[$key];
}
// Allow max 5 submissions per hour
if ($data['count'] >= 5) {
return false;
}
return true;
}
// Input validation and sanitization
function validateInput($data, $type = 'text') {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
// Prevent header injection
$data = str_replace(array("\r", "\n", "%0a", "%0d"), '', $data);
switch ($type) {
case 'email':
$email = filter_var($data, FILTER_VALIDATE_EMAIL);
// Additional email validation to prevent header injection
if ($email && !preg_match('/[\r\n]/', $email)) {
return $email;
}
return false;
case 'phone':
return preg_match('/^[\+]?[0-9\s\-\(\)]+$/', $data) ? $data : false;
case 'text':
return strlen($data) > 0 ? $data : false;
case 'message':
return strlen($data) >= 10 ? $data : false;
default:
return $data;
}
}
// Response function
function sendResponse($success, $message, $data = null) {
$response = [
'success' => $success,
'message' => $message
];
if ($data !== null) {
$response['data'] = $data;
}
echo json_encode($response);
exit;
}
// Handle POST requests only
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
sendResponse(false, 'Invalid request method');
}
// Check referer to prevent external form submissions
$allowed_referers = ['ukdataservices.co.uk', 'www.ukdataservices.co.uk', 'localhost'];
$referer_valid = false;
if (isset($_SERVER['HTTP_REFERER'])) {
$referer_host = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST);
foreach ($allowed_referers as $allowed) {
if ($referer_host === $allowed || strpos($referer_host, $allowed) !== false) {
$referer_valid = true;
break;
}
}
}
// Allow direct access for testing but log it
if (!$referer_valid && !isset($_SERVER['HTTP_REFERER'])) {
error_log("Contact form accessed without referer from IP: " . $_SERVER['REMOTE_ADDR']);
}
// Check for blocked IPs
function checkBlockedIP() {
$ip = $_SERVER['REMOTE_ADDR'];
$blockFile = 'logs/blocked-ips.txt';
if (file_exists($blockFile)) {
$blockedIPs = file($blockFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($blockedIPs as $blockedEntry) {
$parts = explode('|', $blockedEntry);
if (isset($parts[0]) && $parts[0] === $ip) {
$blockTime = isset($parts[1]) ? (int)$parts[1] : 0;
// Block for 24 hours
if (time() - $blockTime < 86400) {
return false;
}
}
}
}
return true;
}
// Check for blocked IPs first
if (!checkBlockedIP()) {
sendResponse(false, 'Access temporarily restricted');
}
2025-06-08 11:21:30 +01:00
// Check rate limiting
if (!checkRateLimit()) {
sendResponse(false, 'Too many requests. Please try again later.');
}
2025-06-17 19:22:58 +01:00
// Verify reCAPTCHA v3
if (isset($_POST['recaptcha_response'])) {
$recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify';
$recaptcha_secret = RECAPTCHA_SECRET_KEY;
$recaptcha_response = $_POST['recaptcha_response'];
$recaptcha_data = array(
'secret' => $recaptcha_secret,
'response' => $recaptcha_response,
'remoteip' => $_SERVER['REMOTE_ADDR']
);
$recaptcha_options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($recaptcha_data)
)
);
$recaptcha_context = stream_context_create($recaptcha_options);
$recaptcha_result = @file_get_contents($recaptcha_url, false, $recaptcha_context);
if ($recaptcha_result === false) {
// Log reCAPTCHA API failure but don't block submission
error_log("reCAPTCHA API call failed for IP: " . $_SERVER['REMOTE_ADDR']);
} else {
$recaptcha_json = json_decode($recaptcha_result, true);
// Temporarily disable reCAPTCHA check for test keys
if (!$recaptcha_json['success']) {
// Log suspicious activity but don't block for test keys
$logEntry = date('Y-m-d H:i:s') . " - RECAPTCHA WARNING: " . json_encode($recaptcha_json) . " from " . $_SERVER['REMOTE_ADDR'] . "\n";
file_put_contents('logs/contact-errors.log', $logEntry, FILE_APPEND | LOCK_EX);
}
// Only block if score is extremely low and not using test keys
if (isset($recaptcha_json['score']) && $recaptcha_json['score'] < 0.1 && RECAPTCHA_SITE_KEY !== '6LcdAtUUAAAAAPX-5YJaWKJmeq7QIMjeLTS7qy6s') {
$logEntry = date('Y-m-d H:i:s') . " - RECAPTCHA BLOCKED: Score " . $recaptcha_json['score'] . " from " . $_SERVER['REMOTE_ADDR'] . "\n";
2025-06-17 19:22:58 +01:00
file_put_contents('logs/contact-errors.log', $logEntry, FILE_APPEND | LOCK_EX);
sendResponse(false, 'Security verification failed. Please try again.');
}
}
} else {
// No reCAPTCHA response - likely a bot
sendResponse(false, 'Security verification required.');
}
2025-06-08 11:21:30 +01:00
// Validate and sanitize inputs
$name = validateInput($_POST['name'] ?? '', 'text');
$email = validateInput($_POST['email'] ?? '', 'email');
$company = validateInput($_POST['company'] ?? '', 'text');
$service = validateInput($_POST['service'] ?? '', 'text');
$message = validateInput($_POST['message'] ?? '', 'message');
// Validation
$errors = [];
if (!$name || strlen($name) < 2) {
$errors[] = 'Please enter a valid name (minimum 2 characters)';
}
if (!$email) {
$errors[] = 'Please enter a valid email address';
}
if (!$message) {
$errors[] = 'Please provide project details (minimum 10 characters)';
}
if (!empty($errors)) {
sendResponse(false, implode('. ', $errors));
}
// Spam protection - simple honeypot and content filtering
if (isset($_POST['website']) && !empty($_POST['website'])) {
// Honeypot field filled - likely spam
sendResponse(false, 'Spam detected');
}
// Enhanced spam protection - expanded keyword list
$spamKeywords = [
'viagra', 'cialis', 'casino', 'lottery', 'bitcoin', 'forex', 'loan', 'debt',
'pharmacy', 'click here', 'act now', 'limited time', 'risk free', 'guarantee',
'no obligation', 'free money', 'make money fast', 'work from home', 'get rich',
'investment opportunity', 'credit repair', 'refinance', 'consolidate debt',
'weight loss', 'miracle cure', 'lose weight', 'adult content', 'porn',
2025-06-17 19:22:58 +01:00
'sex', 'dating', 'singles', 'webcam', 'escort', 'massage', 'crypto', 'nft',
'blockchain', 'earn money', 'passive income', 'financial freedom', 'mlm',
'network marketing', 'online casino', 'gambling', 'betting', 'binary options'
];
// Check for disposable email domains
$disposableEmailDomains = [
'mailinator.com', 'guerrillamail.com', '10minutemail.com', 'temp-mail.org',
'throwawaymail.com', 'yopmail.com', 'mailnesia.com', 'trashmail.com',
'maildrop.cc', 'mailcatch.com', 'tempmail.com', 'email-fake.com',
'fakeinbox.com', 'sharklasers.com', 'guerrillamailblock.com'
];
2025-06-08 11:21:30 +01:00
2025-06-17 19:22:58 +01:00
$emailDomain = substr(strrchr($email, "@"), 1);
if (in_array(strtolower($emailDomain), $disposableEmailDomains)) {
sendResponse(false, 'Please use a valid business email address.');
}
$messageContent = strtolower($message . ' ' . $name . ' ' . $company);
2025-06-08 11:21:30 +01:00
foreach ($spamKeywords as $keyword) {
if (strpos($messageContent, $keyword) !== false) {
sendResponse(false, 'Invalid content detected');
}
}
// Bot detection - check for suspicious patterns
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
$suspiciousAgents = ['curl', 'wget', 'python', 'bot', 'crawler', 'spider', 'scraper'];
foreach ($suspiciousAgents as $agent) {
if (stripos($userAgent, $agent) !== false) {
sendResponse(false, 'Automated submissions not allowed');
}
}
2025-06-17 18:51:06 +01:00
// Check submission speed (too fast = likely bot) - More lenient timing
if (isset($_SESSION['form_start_time'])) {
$submissionTime = time() - $_SESSION['form_start_time'];
if ($submissionTime < 2) { // Only block if under 2 seconds (very aggressive bots)
sendResponse(false, 'Form submitted too quickly');
}
}
2025-06-17 19:22:58 +01:00
// Check for XMLHttpRequest header (JavaScript submission)
if (!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SERVER['HTTP_X_REQUESTED_WITH'] !== 'XMLHttpRequest') {
// Log direct POST attempt
$logEntry = date('Y-m-d H:i:s') . " - DIRECT POST attempt from " . $_SERVER['REMOTE_ADDR'] . "\n";
file_put_contents('logs/contact-errors.log', $logEntry, FILE_APPEND | LOCK_EX);
sendResponse(false, 'Invalid submission method');
}
// Verify interaction token (human behavior verification)
if (isset($_POST['interaction_token']) && !empty($_POST['interaction_token'])) {
$tokenData = @json_decode(base64_decode($_POST['interaction_token']), true);
if ($tokenData && isset($tokenData['score'])) {
if ($tokenData['score'] < 30) {
// Log low interaction score
$logEntry = date('Y-m-d H:i:s') . " - LOW INTERACTION SCORE: " . $tokenData['score'] . " from " . $_SERVER['REMOTE_ADDR'] . "\n";
file_put_contents('logs/contact-errors.log', $logEntry, FILE_APPEND | LOCK_EX);
sendResponse(false, 'Please complete the form normally');
}
}
}
// Verify form timestamp (prevent replay attacks) - temporarily disabled for debugging
// Timestamp validation is now very lenient - only blocks obviously invalid timestamps
if (isset($_POST['form_timestamp']) && !empty($_POST['form_timestamp'])) {
2025-06-17 19:22:58 +01:00
$formTimestamp = intval($_POST['form_timestamp']);
// Only block if timestamp is 0 or clearly invalid (before year 2020)
if ($formTimestamp > 0 && $formTimestamp < 1577836800000) { // Before Jan 1, 2020
$logEntry = date('Y-m-d H:i:s') . " - INVALID TIMESTAMP: " . $formTimestamp . " from " . $_SERVER['REMOTE_ADDR'] . "\n";
@file_put_contents('logs/contact-errors.log', $logEntry, FILE_APPEND | LOCK_EX);
// Don't block, just log
2025-06-17 19:22:58 +01:00
}
}
// Log all form submissions for debugging
$debugLog = date('Y-m-d H:i:s') . " - DEBUG: timestamp=" . ($_POST['form_timestamp'] ?? 'NOT SET') . ", IP=" . $_SERVER['REMOTE_ADDR'] . "\n";
@file_put_contents('logs/contact-debug.log', $debugLog, FILE_APPEND | LOCK_EX);
2025-06-17 19:22:58 +01:00
2025-06-08 11:21:30 +01:00
// Update rate limit counter
$ip = $_SERVER['REMOTE_ADDR'];
$key = 'contact_' . md5($ip);
$_SESSION[$key]['count']++;
// Prepare email content
$to = 'info@ukdataservices.co.uk';
$subject = 'New Contact Form Submission - UK Data Services';
// Create HTML email
$emailHTML = '
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>New Contact Form Submission</title>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: linear-gradient(135deg, #144784 0%, #179e83 100%); color: white; padding: 20px; text-align: center; }
2025-06-08 11:21:30 +01:00
.content { background: #f9f9f9; padding: 20px; }
.field { margin-bottom: 15px; padding: 10px; background: white; border-left: 4px solid #179e83; }
.field-label { font-weight: bold; color: #144784; }
2025-06-08 11:21:30 +01:00
.footer { text-align: center; padding: 20px; color: #666; font-size: 12px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>New Contact Form Submission</h1>
<p>UK Data Services</p>
</div>
<div class="content">
<div class="field">
<div class="field-label">Name:</div>
<div>' . htmlspecialchars($name) . '</div>
</div>
<div class="field">
<div class="field-label">Email:</div>
<div>' . htmlspecialchars($email) . '</div>
</div>
<div class="field">
<div class="field-label">Company:</div>
<div>' . htmlspecialchars($company ?: 'Not provided') . '</div>
</div>
<div class="field">
<div class="field-label">Service Required:</div>
<div>' . htmlspecialchars($service ?: 'Not specified') . '</div>
</div>
<div class="field">
<div class="field-label">Project Details:</div>
<div>' . nl2br(htmlspecialchars($message)) . '</div>
</div>
<div class="field">
<div class="field-label">Submission Details:</div>
<div>
<strong>IP Address:</strong> ' . htmlspecialchars($_SERVER['REMOTE_ADDR']) . '<br>
<strong>User Agent:</strong> ' . htmlspecialchars($_SERVER['HTTP_USER_AGENT']) . '<br>
<strong>Timestamp:</strong> ' . date('Y-m-d H:i:s') . ' UTC<br>
<strong>Referrer:</strong> ' . htmlspecialchars($_SERVER['HTTP_REFERER'] ?? 'Direct') . '
</div>
</div>
</div>
<div class="footer">
<p>This email was sent from the UK Data Services contact form.</p>
</div>
</div>
</body>
</html>';
// Email headers
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: text/html; charset=UTF-8\r\n";
$headers .= "From: \"UK Data Services Contact Form\" <noreply@ukdataservices.co.uk>\r\n";
$headers .= "Reply-To: " . $email . "\r\n";
$headers .= "X-Mailer: PHP/" . phpversion() . "\r\n";
$headers .= "X-Priority: 3\r\n";
// Create logs directory if it doesn't exist
if (!file_exists('logs')) {
mkdir('logs', 0755, true);
}
// Send email
try {
2025-06-17 18:51:06 +01:00
// Clear any previous errors
error_clear_last();
// First, always log the submission details
$submissionData = [
'timestamp' => date('Y-m-d H:i:s'),
'name' => $name,
'email' => $email,
'company' => $company,
'service' => $service,
'message' => $message,
'ip' => $_SERVER['REMOTE_ADDR'],
'user_agent' => $_SERVER['HTTP_USER_AGENT'],
'referrer' => $_SERVER['HTTP_REFERER'] ?? 'Direct'
];
// Save submission to a JSON file as backup
$submissionFile = 'logs/submissions-' . date('Y-m') . '.json';
$submissions = [];
if (file_exists($submissionFile)) {
$submissions = json_decode(file_get_contents($submissionFile), true) ?? [];
}
$submissions[] = $submissionData;
file_put_contents($submissionFile, json_encode($submissions, JSON_PRETTY_PRINT), LOCK_EX);
// Attempt to send email
$emailSent = @mail($to, $subject, $emailHTML, $headers);
2025-06-08 11:21:30 +01:00
if ($emailSent) {
// Log successful submission
$logEntry = date('Y-m-d H:i:s') . " - Contact form submission from " . $email . " (" . $_SERVER['REMOTE_ADDR'] . ")\n";
file_put_contents('logs/contact-submissions.log', $logEntry, FILE_APPEND | LOCK_EX);
sendResponse(true, 'Thank you for your message! We will get back to you within 24 hours.');
} else {
2025-06-17 18:51:06 +01:00
// Get detailed error information
$lastError = error_get_last();
$errorMsg = $lastError ? $lastError['message'] : 'Mail function returned false';
2025-06-17 18:51:06 +01:00
// Log failed email with detailed error
$logEntry = date('Y-m-d H:i:s') . " - MAIL FAILED but submission saved - from " . $email . " (" . $_SERVER['REMOTE_ADDR'] . ") - Error: " . $errorMsg . "\n";
2025-06-08 11:21:30 +01:00
file_put_contents('logs/contact-errors.log', $logEntry, FILE_APPEND | LOCK_EX);
// Check if mail function exists
if (!function_exists('mail')) {
error_log("PHP mail() function not available");
2025-06-17 18:51:06 +01:00
}
// Still return success since we saved the submission
sendResponse(true, 'Thank you for your message! Your submission has been received and we will get back to you within 24 hours.');
2025-06-08 11:21:30 +01:00
}
} catch (Exception $e) {
2025-06-17 18:51:06 +01:00
// Log exception with full details
$logEntry = date('Y-m-d H:i:s') . " - EXCEPTION: " . $e->getMessage() . " from " . $email . " (" . $_SERVER['REMOTE_ADDR'] . ") - File: " . $e->getFile() . " Line: " . $e->getLine() . "\n";
2025-06-08 11:21:30 +01:00
file_put_contents('logs/contact-errors.log', $logEntry, FILE_APPEND | LOCK_EX);
// Still save the submission
try {
$submissionFile = 'logs/submissions-emergency-' . date('Y-m-d') . '.txt';
$emergencyLog = date('Y-m-d H:i:s') . "\n" .
"Name: " . $name . "\n" .
"Email: " . $email . "\n" .
"Company: " . $company . "\n" .
"Service: " . $service . "\n" .
"Message: " . $message . "\n" .
"IP: " . $_SERVER['REMOTE_ADDR'] . "\n" .
"---\n\n";
file_put_contents($submissionFile, $emergencyLog, FILE_APPEND | LOCK_EX);
sendResponse(true, 'Thank you for your message! Your submission has been received and we will get back to you within 24 hours.');
} catch (Exception $e2) {
sendResponse(false, 'There was an error processing your request. Please contact us directly at info@ukdataservices.co.uk');
}
2025-06-08 11:21:30 +01:00
}
?>