# Phase 1 Security Testing Checklist ## 4WDCSA.co.za - Pre-Go-Live Validation **Date Created:** December 3, 2025 **Status:** READY FOR TESTING **Phase:** 1 - Security & Stability (Weeks 1-3) --- ## 1. CSRF (Cross-Site Request Forgery) Protection ✅ ### Implementation Complete - ✅ CSRF token generation function: `generateCSRFToken()` (64-char hex, 1-hour expiry) - ✅ CSRF token validation: `validateCSRFToken()` (single-use, auto-removal) - ✅ All POST forms include hidden CSRF token field - ✅ All POST processors validate CSRF tokens before processing ### Forms Protected (13 forms) - [x] login.php - User authentication - [x] register.php - New user registration - [x] forgot_password.php - Password reset request - [x] account_settings.php - Account info form - [x] account_settings.php - Password change form - [x] trip-details.php - Trip booking - [x] campsite_booking.php - Campsite booking - [x] course_details.php - Course booking (driver training) - [x] bush_mechanics.php - Course booking (bush mechanics) - [x] driver_training.php - Course booking - [x] comment_box.php - Blog comment submission - [x] membership_application.php - Membership application - [x] campsites.php (modal) - Add campsite form - [x] bar_tabs.php (modal) - Create bar tab form - [x] submit_pop.php - Proof of payment upload ### Backend Processors Protected (12 processors) - [x] validate_login.php - Login validation - [x] register_user.php - User registration - [x] process_booking.php - Booking processing - [x] process_payments.php - Payment processing - [x] process_eft.php - EFT processing - [x] process_application.php - Application processing - [x] process_course_booking.php - Course booking - [x] process_camp_booking.php - Campsite booking - [x] process_trip_booking.php - Trip booking - [x] process_membership_payment.php - Membership payment - [x] process_signature.php - Signature processing - [x] create_bar_tab.php - Bar tab creation - [x] add_campsite.php - Campsite addition - [x] submit_order.php - Order submission ### Test Cases #### Test 1.1: Valid CSRF Token Submission ✅ **Steps:** 1. Load login form (captures CSRF token from form) 2. Fill in credentials 3. Submit form with valid CSRF token in POST data 4. Expected result: Login succeeds **Pass Criteria:** Login processes successfully #### Test 1.2: Missing CSRF Token ❌ **Steps:** 1. Create form request with no csrf_token field 2. POST to login.php 3. Expected result: 403 error, login fails **Pass Criteria:** Response code 403, error message displays #### Test 1.3: Invalid CSRF Token ❌ **Steps:** 1. Load login form 2. Modify csrf_token value to random string 3. Submit form 4. Expected result: 403 error, login fails **Pass Criteria:** Response code 403, error message displays #### Test 1.4: Reused CSRF Token ❌ **Steps:** 1. Load login form, capture csrf_token 2. Submit form once (succeeds) 3. Submit same form again with same token 4. Expected result: 403 error, second submission fails **Pass Criteria:** Second submission rejected #### Test 1.5: Cross-Origin CSRF Attempt ❌ **Steps:** 1. From external domain (e.g., attacker.com), create hidden form targeting 4WDCSA login 2. Attempt to submit without CSRF token 3. Expected result: Failure **Pass Criteria:** Request rejected without valid CSRF token --- ## 2. AUTHENTICATION & SESSION SECURITY ### Implementation Complete - ✅ Session regeneration after successful login - ✅ 30-minute session timeout - ✅ Session cookie security flags (httpOnly, secure, sameSite) - ✅ Password hashing with password_hash() (argon2id) - ✅ Email verification for new accounts ### Test Cases #### Test 2.1: Session Regeneration ✅ **Steps:** 1. Get session ID before login 2. Login successfully 3. Get session ID after login 4. Expected result: Session IDs are different **Pass Criteria:** Session ID changes after login #### Test 2.2: Session Timeout ❌ **Steps:** 1. Login successfully 2. Wait 31 minutes (or manipulate session time) 3. Attempt to access protected page 4. Expected result: Redirected to login **Pass Criteria:** Session expires after 30 minutes #### Test 2.3: Session Fixation Prevention ❌ **Steps:** 1. Pre-generate session ID 2. Create hidden form that sets this session 3. Attempt to login with pre-set session 4. Expected result: Session ID should change anyway **Pass Criteria:** Session regenerates regardless of initial state #### Test 2.4: Cookie Security Headers ✅ **Steps:** 1. Login and inspect response headers 2. Check Set-Cookie header 3. Expected result: httpOnly, secure, sameSite=Strict flags present **Pass Criteria:** All security flags present #### Test 2.5: Plaintext Password Storage ❌ **Steps:** 1. Query users table directly 2. Check password column 3. Expected result: Hashes, not plaintext (should start with $2y$ or $argon2id$) **Pass Criteria:** All passwords are hashed --- ## 3. RATE LIMITING & ACCOUNT LOCKOUT ### Implementation Complete - ✅ Login attempt tracking in login_attempts table - ✅ 5 failed attempts = 30-minute lockout - ✅ IP-based and email-based tracking - ✅ Audit logging of all lockouts ### Test Cases #### Test 3.1: Brute Force Prevention ❌ **Steps:** 1. Attempt login with wrong password 5 times rapidly 2. Attempt 6th login 3. Expected result: Account locked for 30 minutes **Pass Criteria:** 6th attempt blocked with lockout message #### Test 3.2: Lockout Message ℹ️ **Steps:** 1. After 5 failed attempts, inspect error message 2. Expected result: Clear message about lockout and duration **Pass Criteria:** User-friendly lockout message appears #### Test 3.3: Lockout Reset After Timeout ✅ **Steps:** 1. Fail login 5 times 2. Wait 31 minutes (or manipulate database time) 3. Attempt login with correct credentials 4. Expected result: Login succeeds **Pass Criteria:** Lockout expires automatically #### Test 3.4: Successful Login Clears Attempts ✅ **Steps:** 1. Fail login 3 times 2. Login successfully 3. Fail login again 5 times 4. Expected result: Lockout happens on 5th attempt (not 2nd) **Pass Criteria:** Attempt counter resets after successful login #### Test 3.5: IP-Based Rate Limiting ℹ️ **Steps:** 1. From one IP, fail login 5 times 2. From different IP, attempt login 3. Expected result: Different IP should not be blocked **Pass Criteria:** Rate limiting is per-IP, not global --- ## 4. SQL INJECTION PREVENTION ### Implementation Complete - ✅ All queries use prepared statements with parameterized queries - ✅ getResultFromTable() refactored with column/table whitelisting - ✅ Input validation on all user-supplied data - ✅ Audit logging for validation failures ### Test Cases #### Test 4.1: Login SQL Injection ❌ **Steps:** 1. In login form, enter email: `' OR '1'='1` 2. Enter any password 3. Submit 4. Expected result: Login fails, no SQL error reveals **Pass Criteria:** Login rejected, no database info disclosed #### Test 4.2: Booking Date SQL Injection ❌ **Steps:** 1. In booking form, modify date parameter to: `2025-01-01'; DROP TABLE bookings;--` 2. Submit form 3. Expected result: Bookings table still exists, error message appears **Pass Criteria:** Table not dropped, invalid input rejected #### Test 4.3: Comment SQL Injection ❌ **Steps:** 1. In comment box, enter: `' OR '1'='1` 2. Submit comment 3. Expected result: Stored safely as text, no execution **Pass Criteria:** Comment stored but not executed #### Test 4.4: Union-Based SQL Injection ❌ **Steps:** 1. In search field, enter: `'; UNION SELECT user_id, password FROM users;--` 2. Expected result: Query fails, no results **Pass Criteria:** Union injection blocked #### Test 4.5: Prepared Statement Verification ✅ **Steps:** 1. Review process_booking.php code 2. Verify all database queries use $stmt->bind_param() 3. Expected result: No direct variable interpolation in SQL **Pass Criteria:** All queries use prepared statements --- ## 5. XSS (Cross-Site Scripting) PREVENTION ### Implementation Complete - ✅ Output encoding with htmlspecialchars() - ✅ Input validation on all form fields - ✅ Content Security Policy headers (recommended) ### Test Cases #### Test 5.1: Stored XSS in Comments ❌ **Steps:** 1. In comment form, enter: `` 2. Submit comment 3. View blog post 4. Expected result: Script does NOT execute, appears as text **Pass Criteria:** Script tag appears as text, no alert() #### Test 5.2: Reflected XSS in Search ❌ **Steps:** 1. Navigate to search page with: `?search=` 2. Expected result: No alert, image tag fails, text displays **Pass Criteria:** No JavaScript execution #### Test 5.3: DOM-Based XSS in Member Details ❌ **Steps:** 1. In member info form, enter name: `">` 2. Save 3. View member profile 4. Expected result: Name displays with quotes escaped **Pass Criteria:** HTML injection prevented #### Test 5.4: Event Handler XSS ❌ **Steps:** 1. In profile update, attempt: `onload=alert('xss')` 2. Submit 3. Expected result: onload attribute removed or escaped **Pass Criteria:** Event handlers sanitized #### Test 5.5: Data Attribute XSS ❌ **Steps:** 1. In form, enter: `
` 2. Submit 3. Expected result: Safe storage, no execution **Pass Criteria:** Data attributes safely stored --- ## 6. FILE UPLOAD VALIDATION ### Implementation Complete - ✅ Hardcoded MIME type whitelist per file type - ✅ File size limits enforced (5MB images, 10MB documents) - ✅ Extension validation - ✅ Double extension prevention - ✅ Random filename generation - ✅ is_uploaded_file() verification - ✅ Image validation with getimagesize() ### Test Cases #### Test 6.1: Malicious File Extension ❌ **Steps:** 1. Attempt to upload shell.php.jpg (PHP shell with JPG extension) 2. Expected result: Upload rejected **Pass Criteria:** Double extension detected and blocked #### Test 6.2: Executable File Upload ❌ **Steps:** 1. Attempt to upload shell.exe or shell.sh 2. Expected result: Upload rejected, error message **Pass Criteria:** Executable file types blocked #### Test 6.3: File Size Limit ❌ **Steps:** 1. Create 6MB image file 2. Attempt upload as profile picture (5MB limit) 3. Expected result: Upload rejected **Pass Criteria:** Size limit enforced #### Test 6.4: MIME Type Mismatch ❌ **Steps:** 1. Rename shell.php to shell.jpg 2. Attempt upload 3. Expected result: Upload rejected (MIME type is PHP) **Pass Criteria:** MIME type validation catches mismatch #### Test 6.5: Random Filename Generation ✅ **Steps:** 1. Upload two profile pictures 2. Check uploads directory 3. Expected result: Both have random names, not original **Pass Criteria:** Filenames are randomized #### Test 6.6: Image Validation ✅ **Steps:** 1. Create text file with .jpg extension 2. Attempt to upload as profile picture 3. Expected result: getimagesize() fails, upload rejected **Pass Criteria:** Invalid images rejected #### Test 6.7: File Permissions ✅ **Steps:** 1. Upload a file successfully 2. Check file permissions 3. Expected result: 0644 (readable but not executable) **Pass Criteria:** Files not executable after upload #### Test 6.8: Path Traversal Prevention ❌ **Steps:** 1. Attempt upload with filename: `../../../shell.php` 2. Expected result: Random name assigned, path traversal prevented **Pass Criteria:** Upload location cannot be changed --- ## 7. INPUT VALIDATION ### Implementation Complete - ✅ Email validation (format + length) - ✅ Phone number validation - ✅ Name validation (no special characters) - ✅ Date validation (proper format) - ✅ Amount validation (numeric, reasonable ranges) - ✅ ID number validation (South African format) - ✅ Password strength validation (min 8 chars, special char, number, uppercase) ### Test Cases #### Test 7.1: Invalid Email Format ❌ **Steps:** 1. In registration, enter email: `notanemail` 2. Submit form 3. Expected result: Form rejected with error **Pass Criteria:** Invalid emails rejected #### Test 7.2: Email Too Long ❌ **Steps:** 1. In registration, enter email with 300+ characters 2. Submit form 3. Expected result: Form rejected with error **Pass Criteria:** Email length limit enforced #### Test 7.3: Phone Number Validation ❌ **Steps:** 1. In application form, enter phone: `abc123` 2. Submit 3. Expected result: Form rejected **Pass Criteria:** Non-numeric phones rejected #### Test 7.4: Name with SQL Characters ❌ **Steps:** 1. In application, enter name: `O'Brien'; DROP TABLE--` 2. Submit 3. Expected result: Name safely stored without SQL execution **Pass Criteria:** Special characters handled safely #### Test 7.5: Invalid Date Format ❌ **Steps:** 1. In booking form, enter date: `32/13/2025` 2. Submit 3. Expected result: Form rejected with error **Pass Criteria:** Invalid dates rejected #### Test 7.6: Weak Password ❌ **Steps:** 1. In registration, enter password: `password123` 2. Submit 3. Expected result: Form rejected (needs uppercase, special char) **Pass Criteria:** Weak passwords rejected #### Test 7.7: Password Strength Check ✅ **Steps:** 1. Enter password: `SecureP@ssw0rd` 2. Expected result: Password accepted **Pass Criteria:** Strong passwords accepted #### Test 7.8: Negative Amount Submission ❌ **Steps:** 1. In booking, attempt to set amount to `-100` 2. Submit 3. Expected result: Invalid amount rejected **Pass Criteria:** Negative amounts blocked --- ## 8. AUDIT LOGGING & MONITORING ### Implementation Complete - ✅ auditLog() function logs all security events - ✅ audit_log table stores: user_id, action, table, record_id, details, timestamp - ✅ Failed login attempts logged - ✅ CSRF failures logged - ✅ Failed validations logged - ✅ File upload operations logged - ✅ Admin actions logged ### Test Cases #### Test 8.1: Login Attempt Logged ✅ **Steps:** 1. Perform successful login 2. Query audit_log table 3. Expected result: LOGIN_SUCCESS entry present **Pass Criteria:** Login logged with timestamp #### Test 8.2: Failed Login Attempt Logged ✅ **Steps:** 1. Attempt login with wrong password 2. Query audit_log table 3. Expected result: LOGIN_FAILED entry present **Pass Criteria:** Failed login logged #### Test 8.3: CSRF Failure Logged ✅ **Steps:** 1. Submit form with invalid CSRF token 2. Query audit_log table 3. Expected result: CSRF_VALIDATION_FAILED entry **Pass Criteria:** CSRF failures tracked #### Test 8.4: File Upload Logged ✅ **Steps:** 1. Upload profile picture 2. Query audit_log table 3. Expected result: PROFILE_PIC_UPLOAD entry with filename **Pass Criteria:** Uploads tracked with details #### Test 8.5: Audit Log Queryable ℹ️ **Steps:** 1. Admin queries audit log for specific user 2. View all actions performed by user 3. Expected result: Complete action history visible **Pass Criteria:** Audit trail is complete and accessible --- ## 9. DATABASE SECURITY ### Implementation Complete - ✅ Database user with limited privileges (no DROP, no ALTER) - ✅ Prepared statements throughout - ✅ login_attempts table for rate limiting - ✅ audit_log table for security events - ✅ users.locked_until column for account lockout ### Test Cases #### Test 9.1: Database User Permissions ✅ **Steps:** 1. Connect as database user (not admin) 2. Attempt to DROP table 3. Expected result: Permission denied **Pass Criteria:** Database user cannot drop tables #### Test 9.2: Backup Encryption ℹ️ **Steps:** 1. Check database backup location 2. Verify backups are encrypted 3. Expected result: Backups not readable without key **Pass Criteria:** Backups secured #### Test 9.3: Connection Encryption ℹ️ **Steps:** 1. Check database connection settings 2. Verify SSL/TLS enabled 3. Expected result: Database uses encrypted connection **Pass Criteria:** Database traffic encrypted --- ## 10. DEPLOYMENT & CONFIGURATION SECURITY ### Implementation Needed Before Go-Live - [ ] Remove phpinfo() calls - [ ] Hide error messages from users (log to file instead) - [ ] Set error_reporting to E_ALL but display_errors = Off - [ ] Remove debug code and print_r() statements - [ ] Update .htaccess to disable directory listing - [ ] Set proper file permissions (644 for PHP, 755 for directories) - [ ] Verify HTTPS enforced on all pages - [ ] Update robots.txt to allow search engines - [ ] Review sensitive file access (no direct access to uploads) - [ ] Set Content-Security-Policy headers ### Pre-Go-Live Checklist - [ ] phpinfo.php deleted - [ ] testenv.php deleted - [ ] env.php contains production credentials - [ ] Database backups configured and tested - [ ] Backup restoration procedure documented - [ ] Incident response plan documented - [ ] Admin contact information documented --- ## 11. PERFORMANCE & STABILITY ### Implementation Complete - ✅ Database queries optimized with indexes - ✅ Session cleanup for expired CSRF tokens - ✅ Error handling prevents partial failures ### Test Cases #### Test 11.1: Large Comment Load ✅ **Steps:** 1. Load blog post with 1000+ comments 2. Measure page load time 3. Expected result: Loads within 3 seconds **Pass Criteria:** Performance acceptable #### Test 11.2: Concurrent User Stress ✅ **Steps:** 1. Simulate 50 concurrent users logging in 2. Monitor database connections 3. Expected result: No timeouts, all succeed **Pass Criteria:** System handles load #### Test 11.3: Session Cleanup ✅ **Steps:** 1. Generate 1000 CSRF tokens 2. Wait for expiration (1 hour) 3. Check session size 4. Expected result: Session not bloated, tokens cleaned **Pass Criteria:** Cleanup occurs properly --- ## 12. GO-LIVE SECURITY SIGN-OFF ### Requirements Before Production Deployment #### Security Review ✅ - [ ] All 11 Phase 1 tasks completed and tested - [ ] No known security vulnerabilities - [ ] Audit log functional and accessible - [ ] Backup and recovery tested - [ ] Incident response plan documented #### Code Review ✅ - [ ] No debug code in production files - [ ] No direct SQL queries (all parameterized) - [ ] No hardcoded credentials - [ ] All error messages user-friendly - [ ] HTTPS enforced on all pages #### Deployment Review ✅ - [ ] Database migrated successfully - [ ] All tables created with proper indexes - [ ] File permissions set correctly (644/755) - [ ] Upload directories outside web root (if possible) - [ ] Backups configured and tested - [ ] Monitoring/logging configured #### User Communication ✅ - [ ] Security policy documented and communicated - [ ] Password requirements communicated - [ ] MFA/email verification process clear - [ ] Incident contact information provided - [ ] Data privacy policy updated --- ## 13. SIGN-OFF ### Tested By - **QA Team:** _________________________ Date: _________ - **Security Team:** _________________________ Date: _________ - **Project Manager:** _________________________ Date: _________ ### Approved For Deployment - **Authorized By:** _________________________ Date: _________ - **Title:** _________________________________ ### Notes & Issues ``` [Space for any issues found and resolutions] ``` --- ## Next Steps After Phase 1 (Phase 2 - Hardening) 1. **Implement Web Application Firewall (WAF)** - Add ModSecurity or equivalent - Block known attack patterns 2. **Add Rate Limiting at HTTP Level** - Prevent DDoS attacks - Limit API requests per IP 3. **Implement Content Security Policy (CSP)** - Restrict script sources - Prevent inline script execution 4. **Add Database Connection Pooling** - Replace global $conn with connection pool - Improve performance under load 5. **Implement API Authentication** - Add JWT or OAuth for API calls - Secure AJAX requests 6. **Add Security Headers** - X-Frame-Options: DENY - X-Content-Type-Options: nosniff - Strict-Transport-Security: max-age=31536000 7. **Automated Security Testing** - Add OWASP ZAP to CI/CD pipeline - Automated SQL injection testing - Automated XSS testing --- **End of Security Testing Checklist**