Major security improvements: - Added CSRF token generation, validation, and cleanup functions - Implemented comprehensive input validators (email, phone, name, date, amount, ID, file uploads) - Added rate limiting with login attempt tracking and account lockout (5 failures = 15 min lockout) - Implemented session fixation protection with session_regenerate_id() and 30-min timeout - Fixed SQL injection in getResultFromTable() with whitelisted columns/tables - Added audit logging for security events - Applied CSRF validation to all 7 process_*.php files - Applied input validation to critical endpoints (login, registration, bookings, application) - Created database migration for login_attempts, audit_log tables and locked_until column Modified files: - functions.php: +500 lines of security functions - validate_login.php: Added CSRF, rate limiting, session hardening - register_user.php: Added CSRF, input validation, registration rate limiting - process_*.php (7 files): Added CSRF token validation - Created migration: 001_phase1_security_schema.sql Next steps: Add CSRF tokens to form templates, harden file uploads, create testing checklist
200 lines
8.6 KiB
PHP
200 lines
8.6 KiB
PHP
<?php
|
|
require_once("env.php");
|
|
require_once("session.php");
|
|
require_once("connection.php");
|
|
require_once("functions.php");
|
|
|
|
$user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : null;
|
|
$eft_id = strtoupper($user_id." SUBS ".date("Y")." ".getInitialSurname($user_id));
|
|
$status = 'AWAITING PAYMENT';
|
|
$description = 'Membership Fees '.date("Y")." ".getInitialSurname($user_id);
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
// CSRF Token Validation
|
|
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
|
|
auditLog($user_id, 'CSRF_VALIDATION_FAILED', 'membership_application', null, ['endpoint' => 'process_application.php']);
|
|
http_response_code(403);
|
|
die('Security token validation failed. Please try again.');
|
|
}
|
|
|
|
// Get all the form fields with validation
|
|
$first_name = validateName($_POST['first_name'] ?? '');
|
|
if ($first_name === false) {
|
|
die('Invalid first name format.');
|
|
}
|
|
|
|
$last_name = validateName($_POST['last_name'] ?? '');
|
|
if ($last_name === false) {
|
|
die('Invalid last name format.');
|
|
}
|
|
|
|
$id_number = validateSAIDNumber($_POST['id_number'] ?? '');
|
|
if ($id_number === false) {
|
|
die('Invalid ID number format.');
|
|
}
|
|
|
|
$dob = validateDate($_POST['dob'] ?? '');
|
|
if ($dob === false) {
|
|
die('Invalid date of birth format.');
|
|
}
|
|
|
|
$occupation = sanitizeTextInput($_POST['occupation'] ?? '', 100);
|
|
$tel_cell = validatePhoneNumber($_POST['tel_cell'] ?? '');
|
|
if ($tel_cell === false) {
|
|
die('Invalid phone number format.');
|
|
}
|
|
|
|
$email = validateEmail($_POST['email'] ?? '');
|
|
if ($email === false) {
|
|
die('Invalid email format.');
|
|
}
|
|
|
|
// Spouse or Partner details (optional)
|
|
$spouse_first_name = !empty($_POST['spouse_first_name']) ? validateName($_POST['spouse_first_name']) : null;
|
|
$spouse_last_name = !empty($_POST['spouse_last_name']) ? validateName($_POST['spouse_last_name']) : null;
|
|
$spouse_id_number = !empty($_POST['spouse_id_number']) ? validateSAIDNumber($_POST['spouse_id_number']) : null;
|
|
$spouse_dob = !empty($_POST['spouse_dob']) ? validateDate($_POST['spouse_dob']) : NULL;
|
|
$spouse_occupation = !empty($_POST['spouse_occupation']) ? sanitizeTextInput($_POST['spouse_occupation'], 100) : null;
|
|
$spouse_tel_cell = !empty($_POST['spouse_tel_cell']) ? validatePhoneNumber($_POST['spouse_tel_cell']) : null;
|
|
$spouse_email = !empty($_POST['spouse_email']) ? validateEmail($_POST['spouse_email']) : null;
|
|
|
|
// Children details (optional)
|
|
$child_name1 = !empty($_POST['child_name1']) ? $_POST['child_name1'] : null;
|
|
$child_dob1 = !empty($_POST['child_dob1']) ? $_POST['child_dob1'] : null;
|
|
$child_name2 = !empty($_POST['child_name2']) ? $_POST['child_name2'] : null;
|
|
$child_dob2 = !empty($_POST['child_dob2']) ? $_POST['child_dob2'] : null;
|
|
$child_name3 = !empty($_POST['child_name3']) ? $_POST['child_name3'] : null;
|
|
$child_dob3 = !empty($_POST['child_dob3']) ? $_POST['child_dob3'] : null;
|
|
|
|
// Address and other details
|
|
$physical_address = $_POST['physical_address'];
|
|
$postal_address = $_POST['postal_address'];
|
|
$interests_hobbies = $_POST['interests_hobbies'];
|
|
|
|
// Primary vehicle details
|
|
$vehicle_make = $_POST['vehicle_make'];
|
|
$vehicle_model = $_POST['vehicle_model'];
|
|
$vehicle_year = $_POST['vehicle_year'];
|
|
$vehicle_registration = $_POST['vehicle_registration'];
|
|
|
|
// Secondary vehicle details (optional)
|
|
$secondary_vehicle_make = !empty($_POST['secondary_vehicle_make']) ? $_POST['secondary_vehicle_make'] : null;
|
|
$secondary_vehicle_model = !empty($_POST['secondary_vehicle_model']) ? $_POST['secondary_vehicle_model'] : null;
|
|
$secondary_vehicle_year = !empty($_POST['secondary_vehicle_year']) ? $_POST['secondary_vehicle_year'] : null;
|
|
$secondary_vehicle_registration = !empty($_POST['secondary_vehicle_registration']) ? $_POST['secondary_vehicle_registration'] : null;
|
|
|
|
// Start a transaction to ensure data consistency
|
|
$conn->begin_transaction();
|
|
|
|
try {
|
|
// Insert into the member application table
|
|
$stmt = $conn->prepare("INSERT INTO membership_application (
|
|
user_id, first_name, last_name, id_number, dob, occupation, tel_cell, email,
|
|
spouse_first_name, spouse_last_name, spouse_id_number, spouse_dob, spouse_occupation, spouse_tel_cell, spouse_email,
|
|
child_name1, child_dob1, child_name2, child_dob2, child_name3, child_dob3,
|
|
physical_address, postal_address, interests_hobbies, vehicle_make, vehicle_model, vehicle_year, vehicle_registration,
|
|
secondary_vehicle_make, secondary_vehicle_model, secondary_vehicle_year, secondary_vehicle_registration
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
|
|
|
// Check if preparation was successful
|
|
if (!$stmt) {
|
|
die("SQL error: " . $conn->error);
|
|
}
|
|
|
|
$stmt->bind_param(
|
|
"isssssssssssssssssssssssssssssss",
|
|
$user_id,
|
|
$first_name,
|
|
$last_name,
|
|
$id_number,
|
|
$dob,
|
|
$occupation,
|
|
$tel_cell,
|
|
$email,
|
|
$spouse_first_name,
|
|
$spouse_last_name,
|
|
$spouse_id_number,
|
|
$spouse_dob,
|
|
$spouse_occupation,
|
|
$spouse_tel_cell,
|
|
$spouse_email,
|
|
$child_name1,
|
|
$child_dob1,
|
|
$child_name2,
|
|
$child_dob2,
|
|
$child_name3,
|
|
$child_dob3,
|
|
$physical_address,
|
|
$postal_address,
|
|
$interests_hobbies,
|
|
$vehicle_make,
|
|
$vehicle_model,
|
|
$vehicle_year,
|
|
$vehicle_registration,
|
|
$secondary_vehicle_make,
|
|
$secondary_vehicle_model,
|
|
$secondary_vehicle_year,
|
|
$secondary_vehicle_registration
|
|
);
|
|
|
|
if ($stmt->execute()) {
|
|
// Insert into the membership fees table
|
|
$payment_amount = calculateProrata(210); // Assuming a fixed membership fee, adjust as needed
|
|
$payment_date = date('Y-m-d');
|
|
$membership_start_date = $payment_date;
|
|
// $membership_end_date = date('Y-12-31');
|
|
|
|
// Get today's date
|
|
$today = new DateTime();
|
|
|
|
// Determine the target February
|
|
if ($today->format('n') > 2) {
|
|
// If we're past February, target is next year's Feb 28/29
|
|
$year = $today->format('Y') + 1;
|
|
} else {
|
|
// Otherwise, this year's February
|
|
$year = $today->format('Y');
|
|
}
|
|
|
|
// Handle leap year (Feb 29) automatically
|
|
$membership_end_date = (new DateTime("$year-02-01"))
|
|
->modify('last day of this month')
|
|
->format('Y-m-d');
|
|
|
|
$stmt = $conn->prepare("INSERT INTO membership_fees (user_id, payment_amount, payment_date, membership_start_date, membership_end_date, payment_status, payment_id)
|
|
VALUES (?, ?, ?, ?, ?, 'PENDING', ?)");
|
|
$stmt->bind_param("idssss", $user_id, $payment_amount, $payment_date, $membership_start_date, $membership_end_date, $eft_id);
|
|
|
|
if ($stmt->execute()) {
|
|
// Commit the transaction
|
|
$conn->commit();
|
|
addSubsEFT($eft_id, $user_id, $status, $payment_amount, $description);
|
|
sendAdminNotification('4WDCSA.co.za - New Membership Application - '.$last_name , 'A new member has signed up, '.$first_name.' '.$last_name);
|
|
header("Location:indemnity.php");
|
|
// Success message
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'Your membership application has been submitted successfully!'
|
|
];
|
|
} else {
|
|
throw new Exception("Failed to insert membership fee. SQL error: " . $conn->error);
|
|
}
|
|
} else {
|
|
throw new Exception("Failed to insert member application.SQL error: " . $conn->error);
|
|
}
|
|
} catch (Exception $e) {
|
|
// Rollback the transaction in case of error
|
|
$conn->rollback();
|
|
|
|
// Error response
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Error: ' . $e->getMessage()
|
|
];
|
|
}
|
|
|
|
// Return the response in JSON format
|
|
echo json_encode($response);
|
|
}
|
|
?>
|