Fix consultation form CSRF validation and improve site optimization
- Fix CSRF token validation for quote form by adding proper session handling and credentials to AJAX requests - Replace alert boxes with styled error/success messages and HTML response pages for better UX - Add visual indicators (*) to required form fields for better accessibility - Standardize navigation links to use absolute paths for consistency - Update JavaScript references to use minified version (main.min.js) for better performance - Archive large error log file and clean up sitemap by removing broken image references - Add comprehensive documentation to reCAPTCHA configuration about test vs production keys - Update sitemap lastmod dates to current timestamp 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
161
quote.php
161
quote.php
@@ -1,4 +1,10 @@
|
||||
<?php
|
||||
// Start session before any output
|
||||
ini_set('session.cookie_samesite', 'Lax');
|
||||
ini_set('session.cookie_httponly', '1');
|
||||
ini_set('session.cookie_secure', '0'); // Set to '1' if using HTTPS
|
||||
session_start();
|
||||
|
||||
// Enhanced security headers
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
header('X-Frame-Options: DENY');
|
||||
@@ -214,6 +220,73 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/* Required field indicators */
|
||||
.required-indicator {
|
||||
color: #e74c3c;
|
||||
font-weight: bold;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
/* Error and success messages */
|
||||
.message-container {
|
||||
margin-bottom: 30px;
|
||||
padding: 15px 20px;
|
||||
border-radius: 8px;
|
||||
display: none;
|
||||
animation: slideDown 0.3s ease;
|
||||
}
|
||||
|
||||
.message-container.error {
|
||||
background: #fee;
|
||||
border: 1px solid #fcc;
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
.message-container.success {
|
||||
background: #efe;
|
||||
border: 1px solid #cfc;
|
||||
color: #060;
|
||||
}
|
||||
|
||||
.message-container.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.message-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.message-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.message-list li {
|
||||
padding: 5px 0;
|
||||
padding-left: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.message-list li:before {
|
||||
content: '\2022';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.step-title {
|
||||
font-size: 1.4rem;
|
||||
font-weight: 600;
|
||||
@@ -414,12 +487,20 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
||||
<section class="quote-form-section">
|
||||
<div class="container">
|
||||
<div class="quote-form-container">
|
||||
<!-- Message Container -->
|
||||
<div id="message-container" class="message-container">
|
||||
<div class="message-title"></div>
|
||||
<ul class="message-list"></ul>
|
||||
</div>
|
||||
|
||||
<form id="quote-form" action="quote-handler.php" method="POST">
|
||||
<?php
|
||||
session_start();
|
||||
// Session already started at top of file
|
||||
if (!isset($_SESSION['csrf_token'])) {
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
// Set form start time for bot detection
|
||||
$_SESSION['form_start_time'] = time();
|
||||
?>
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
|
||||
<!-- Honeypot field for spam protection -->
|
||||
@@ -428,7 +509,7 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
||||
<div class="form-step">
|
||||
<div class="step-title">
|
||||
<span class="step-number">1</span>
|
||||
What type of data services do you need?
|
||||
What type of data services do you need? <span class="required-indicator">*</span>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<label class="checkbox-item">
|
||||
@@ -462,7 +543,7 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
||||
<div class="form-step">
|
||||
<div class="step-title">
|
||||
<span class="step-number">2</span>
|
||||
What's the scale of your project?
|
||||
What's the scale of your project? <span class="required-indicator">*</span>
|
||||
</div>
|
||||
<div class="radio-group">
|
||||
<label class="radio-item">
|
||||
@@ -500,7 +581,7 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
||||
<div class="form-step">
|
||||
<div class="step-title">
|
||||
<span class="step-number">3</span>
|
||||
When do you need this completed?
|
||||
When do you need this completed? <span class="required-indicator">*</span>
|
||||
</div>
|
||||
<div class="radio-group">
|
||||
<label class="radio-item">
|
||||
@@ -671,6 +752,44 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
||||
});
|
||||
});
|
||||
|
||||
// Message display functions
|
||||
function showMessage(type, title, messages) {
|
||||
const container = document.getElementById('message-container');
|
||||
const titleEl = container.querySelector('.message-title');
|
||||
const listEl = container.querySelector('.message-list');
|
||||
|
||||
// Reset classes
|
||||
container.className = 'message-container';
|
||||
container.classList.add(type);
|
||||
|
||||
// Set content
|
||||
titleEl.textContent = title;
|
||||
listEl.innerHTML = '';
|
||||
|
||||
if (Array.isArray(messages)) {
|
||||
messages.forEach(msg => {
|
||||
const li = document.createElement('li');
|
||||
li.textContent = msg;
|
||||
listEl.appendChild(li);
|
||||
});
|
||||
} else {
|
||||
const li = document.createElement('li');
|
||||
li.textContent = messages;
|
||||
listEl.appendChild(li);
|
||||
}
|
||||
|
||||
// Show with animation
|
||||
container.classList.add('show');
|
||||
|
||||
// Scroll to message
|
||||
container.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
}
|
||||
|
||||
function hideMessage() {
|
||||
const container = document.getElementById('message-container');
|
||||
container.classList.remove('show');
|
||||
}
|
||||
|
||||
// Enhanced form submission with reCAPTCHA
|
||||
const quoteForm = document.getElementById('quote-form');
|
||||
|
||||
@@ -767,10 +886,13 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
alert('Please fix the following errors:\n\n' + errors.join('\n'));
|
||||
showMessage('error', 'Please complete the following required fields:', errors);
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide any existing error messages
|
||||
hideMessage();
|
||||
|
||||
// Execute reCAPTCHA
|
||||
const self = this;
|
||||
grecaptcha.ready(function() {
|
||||
@@ -788,6 +910,7 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
||||
fetch('quote-handler.php', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
@@ -795,7 +918,7 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert('Thank you! Your quote request has been sent. We will get back to you within 24 hours with a detailed proposal.');
|
||||
showMessage('success', 'Thank you!', 'Your quote request has been sent successfully. We will get back to you within 24 hours with a detailed proposal.');
|
||||
self.reset();
|
||||
// Reset interaction tracking
|
||||
formInteractions = {
|
||||
@@ -805,16 +928,26 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
||||
startTime: Date.now(),
|
||||
fields: {}
|
||||
};
|
||||
// Reset styling
|
||||
checkboxItems.forEach(item => item.classList.remove('checked'));
|
||||
radioItems.forEach(item => item.classList.remove('checked'));
|
||||
} else {
|
||||
alert('Error: ' + data.message);
|
||||
}
|
||||
})
|
||||
// Reset styling
|
||||
checkboxItems.forEach(item => item.classList.remove('checked'));
|
||||
radioItems.forEach(item => item.classList.remove('checked'));
|
||||
|
||||
// Scroll to top after success
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
} else {
|
||||
// Parse error message if it contains multiple validation errors
|
||||
let errorMessages = [];
|
||||
if (data.message.includes('. ')) {
|
||||
errorMessages = data.message.split('. ').filter(msg => msg.trim());
|
||||
} else {
|
||||
errorMessages = [data.message];
|
||||
}
|
||||
showMessage('error', 'There was a problem with your submission:', errorMessages);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
alert('There was an error sending your quote request. Please try again or contact us directly.');
|
||||
showMessage('error', 'Connection Error', 'There was an error sending your quote request. Please check your internet connection and try again, or contact us directly at info@ukdataservices.co.uk');
|
||||
})
|
||||
.finally(() => {
|
||||
submitButton.textContent = originalText;
|
||||
|
||||
Reference in New Issue
Block a user