Files
ukaiautomation/contact-handler.php
2025-06-17 19:22:58 +01:00

437 lines
16 KiB
PHP

<?php
// Enhanced Contact Form Handler with Security
session_start();
// Include reCAPTCHA config
require_once '.recaptcha-config.php';
// Security headers
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block');
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');
}
// Check rate limiting
if (!checkRateLimit()) {
sendResponse(false, 'Too many requests. Please try again later.');
}
// 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);
if (!$recaptcha_json['success'] || $recaptcha_json['score'] < RECAPTCHA_THRESHOLD) {
// Log suspicious activity and block
$logEntry = date('Y-m-d H:i:s') . " - RECAPTCHA FAILED: Score " . ($recaptcha_json['score'] ?? '0') . " from " . $_SERVER['REMOTE_ADDR'] . "\n";
file_put_contents('logs/contact-errors.log', $logEntry, FILE_APPEND | LOCK_EX);
// Add to blocked IPs if score is very low
if (isset($recaptcha_json['score']) && $recaptcha_json['score'] < 0.3) {
$blockEntry = $_SERVER['REMOTE_ADDR'] . '|' . time() . "\n";
file_put_contents('logs/blocked-ips.txt', $blockEntry, FILE_APPEND | LOCK_EX);
}
sendResponse(false, 'Security verification failed. Please try again.');
}
}
} else {
// No reCAPTCHA response - likely a bot
sendResponse(false, 'Security verification required.');
}
// 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',
'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'
];
$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);
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');
}
}
// 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');
}
}
// 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)
if (isset($_POST['form_timestamp'])) {
$formTimestamp = intval($_POST['form_timestamp']);
$currentTime = time() * 1000; // Convert to milliseconds
$timeDiff = $currentTime - $formTimestamp;
// Form older than 1 hour or from the future
if ($timeDiff > 3600000 || $timeDiff < 0) {
sendResponse(false, 'Form session expired. Please refresh and try again.');
}
}
// 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, #667eea 0%, #764ba2 100%); color: white; padding: 20px; text-align: center; }
.content { background: #f9f9f9; padding: 20px; }
.field { margin-bottom: 15px; padding: 10px; background: white; border-left: 4px solid #667eea; }
.field-label { font-weight: bold; color: #667eea; }
.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 {
// Clear any previous errors
error_clear_last();
$emailSent = mail($to, $subject, $emailHTML, $headers);
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 {
// Get detailed error information
$lastError = error_get_last();
$errorMsg = $lastError ? $lastError['message'] : 'Unknown mail error';
// Log failed email with detailed error
$logEntry = date('Y-m-d H:i:s') . " - FAILED contact form submission from " . $email . " (" . $_SERVER['REMOTE_ADDR'] . ") - Error: " . $errorMsg . "\n";
file_put_contents('logs/contact-errors.log', $logEntry, FILE_APPEND | LOCK_EX);
// Check common issues
if (strpos($errorMsg, 'sendmail') !== false) {
error_log("Mail server configuration issue: " . $errorMsg);
}
sendResponse(false, 'There was an error sending your message. Please try again or contact us directly at info@ukdataservices.co.uk');
}
} catch (Exception $e) {
// 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";
file_put_contents('logs/contact-errors.log', $logEntry, FILE_APPEND | LOCK_EX);
sendResponse(false, 'There was an error processing your request. Please contact us directly at info@ukdataservices.co.uk');
}
?>