Rebrand: UK AI Automation — new domain, logo, indigo colour scheme, AI services content
This commit is contained in:
@@ -1,482 +1,482 @@
|
||||
<?php
|
||||
// Enhanced Contact Form Handler with Security
|
||||
<?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');
|
||||
session_start();
|
||||
|
||||
// Form handler restored - temporary fix removed
|
||||
|
||||
// Include reCAPTCHA config
|
||||
require_once '.recaptcha-config.php';
|
||||
|
||||
// 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');
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// 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";
|
||||
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.');
|
||||
}
|
||||
|
||||
// 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"] < 3) {
|
||||
// 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'])) {
|
||||
$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
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
|
||||
// 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; }
|
||||
.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; }
|
||||
.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();
|
||||
|
||||
// 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);
|
||||
|
||||
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'] : 'Mail function returned false';
|
||||
|
||||
// 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";
|
||||
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");
|
||||
}
|
||||
|
||||
// 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.');
|
||||
}
|
||||
} 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);
|
||||
|
||||
// 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');
|
||||
}
|
||||
}
|
||||
session_start();
|
||||
|
||||
// Form handler restored - temporary fix removed
|
||||
|
||||
// Include reCAPTCHA config
|
||||
require_once '.recaptcha-config.php';
|
||||
|
||||
// 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 = ['ukaiautomation.co.uk', 'www.ukaiautomation.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);
|
||||
|
||||
// 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";
|
||||
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.');
|
||||
}
|
||||
|
||||
// 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"] < 3) {
|
||||
// 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'])) {
|
||||
$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
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
|
||||
// Update rate limit counter
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
$key = 'contact_' . md5($ip);
|
||||
$_SESSION[$key]['count']++;
|
||||
|
||||
// Prepare email content
|
||||
$to = 'info@ukaiautomation.co.uk';
|
||||
$subject = 'New Contact Form Submission - UK AI Automation';
|
||||
|
||||
// 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, #7c3aed 0%, #6d28d9 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 #6d28d9; }
|
||||
.field-label { font-weight: bold; color: #7c3aed; }
|
||||
.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 AI Automation</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 AI Automation 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 AI Automation Contact Form\" <noreply@ukaiautomation.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();
|
||||
|
||||
// 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);
|
||||
|
||||
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'] : 'Mail function returned false';
|
||||
|
||||
// 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";
|
||||
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");
|
||||
}
|
||||
|
||||
// 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.');
|
||||
}
|
||||
} 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);
|
||||
|
||||
// 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@ukaiautomation.co.uk');
|
||||
}
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user