================================================================================ SECURITY AUDIT & INCIDENT RESPONSE LOG — April 15, 2026 ================================================================================ Incident: SQL injection attack resulting in dropped database tables. Audit scope: Full codebase security review of all public-facing PHP pages, API endpoints, admin panels, install scripts, and debug utilities. ================================================================================ CRITICAL VULNERABILITIES FOUND & FIXED ================================================================================ [FIX 1] SQL INJECTION — api/search-users.php (PUBLIC, NO AUTH REQUIRED) Severity: CRITICAL Issue: LIMIT clause concatenated directly into prepared statement string. Attacker could inject SQL after LIMIT: ?limit=1 UNION SELECT... Vulnerable code: LIMIT " . $limit . " $stmt->execute([$search, $search]); Fix applied: LIMIT ? $stmt->execute([$search, $search, $limit]); Also fixed: Error message leaked $e->getMessage() to public users. [FIX 2] SQL INJECTION — api/chat-messages.php (PUBLIC, NO AUTH REQUIRED) Severity: CRITICAL Issue: LIMIT clause interpolated with {$limit} in SQL string. Vulnerable code: LIMIT {$limit}"; Fix applied: LIMIT ?"; $params[] = $limit; [FIX 3] SQL INJECTION — admin/cam-sessions.php (ADMIN PANEL) Severity: HIGH Issue: $filter from $_GET directly controlled WHERE clause string, then injected via $pdo->query() (not prepare). Filter not whitelisted. Vulnerable code: $filter = $_GET['filter'] ?? 'all'; $where = '1=1'; if ($filter === 'reported') $where = 's.reported = 1'; $countStmt = $pdo->query("SELECT COUNT(*) FROM cam_sessions s WHERE $where"); Fix applied: - Added strict whitelist: ['all', 'reported', 'active'] - Changed $pdo->query() to $pdo->prepare() with parameter binding - LIMIT/OFFSET now use ? placeholders [FIX 4] SQL INJECTION — admin/comments.php (ADMIN PANEL) Severity: HIGH Issue: WHERE clause built from $filter and directly interpolated via $pdo->query(). No filter whitelisting. LIMIT/OFFSET also interpolated. Vulnerable code: $countStmt = $pdo->query("SELECT COUNT(*) as count FROM photo_comments c $whereClause"); $stmt = $pdo->query("... LIMIT $perPage OFFSET $offset"); Fix applied: - Added strict filter whitelist: ['pending', 'approved', 'guest', 'member', 'all'] - All queries now use $pdo->prepare() with ? placeholders - WHERE values, LIMIT, and OFFSET are all parameterized - Bulk action comment_ids array now sanitized with array_map('intval', ...) [FIX 5] SQL INJECTION — admin/staff-ratings.php (ADMIN PANEL) Severity: HIGH Issue: LIMIT and OFFSET interpolated directly in SQL string after prepare(). Vulnerable code: LIMIT $perPage OFFSET $offset $stmt->execute($params); Fix applied: LIMIT ? OFFSET ? $stmt->execute(array_merge($params, [$perPage, $offset])); [FIX 6] SQL INJECTION — admin/celebration-pics.php (ADMIN PANEL) Severity: HIGH Issue: LIMIT and OFFSET interpolated directly in SQL string. Vulnerable code: $stmt = $pdo->prepare("... LIMIT $perPage OFFSET $offset"); $stmt->execute($params); Fix applied: $stmt = $pdo->prepare("... LIMIT ? OFFSET ?"); $stmt->execute(array_merge($params, [$perPage, $offset])); [FIX 7] SQL INJECTION — admin/photo-mod-applications.php (ADMIN PANEL) Severity: HIGH Issue: Multiple SQL injection vectors: a) Stats queries used direct string interpolation with '$s' and $roleFilterClause b) $roleFilter from $_GET injected via $pdo->quote() (bypassable) c) LIMIT/OFFSET interpolated directly Vulnerable code: $roleFilterClause = ($roleFilter !== 'all') ? " AND role_type = " . $pdo->quote($roleFilter) : ""; $st = $pdo->query("SELECT COUNT(*) FROM photo_mod_applications WHERE status = '$s'$roleFilterClause"); LIMIT $perPage OFFSET $offset Fix applied: - Added strict role whitelist: ['all', 'adult', 'mixed', 'minors'] - All stats queries now use $pdo->prepare() with ? placeholders - LIMIT/OFFSET parameterized [FIX 8] SQL INJECTION — admin/ai-activity.php (ADMIN PANEL) Severity: MEDIUM Issue: $source value from $_GET concatenated into LIKE parameter string, potentially allowing wildcard injection. Vulnerable code: $params[] = '%"source":"' . $source . '"%'; Fix applied: - Added escaping for LIKE wildcards (%, _, ") $safeSource = '%"source":"' . str_replace(['%', '_', '"'], ['\\%', '\\_', ''], $source) . '"%'; ================================================================================ AUTHENTICATION BYPASS — UNAUTHENTICATED INSTALL/DEBUG SCRIPTS ================================================================================ These scripts were publicly accessible WITHOUT any login check, allowing ANYONE to execute database-modifying operations. This is the most likely attack vector for the table-dropping incident. [FIX 9] .htaccess — BLOCKED ALL INSTALL/FIX/DEBUG SCRIPTS AT WEB SERVER LEVEL Severity: CRITICAL Added RewriteRules to return 403 Forbidden for ALL matching patterns: - install[-_]*.php (50+ scripts) - fix[-_]*.php (5+ scripts) - debug[-_]*.php (4 scripts) - emergency[-_]*.php (1 script) - add[-_]*.php (2 scripts) - create[-_]*.php (1 script) - diagnose[-_]*.php (1 script) - restore[-_]*.php (1 script) - aireviewinstall.php - googleinstall.php - addquestions.php - import.php - clear[-_]*.php - generate[-_]*.php [FIX 10] AUTH GUARDS ADDED (defense-in-depth) to these critical scripts: Each now requires admin login (isLoggedIn() && isAdmin()): - emergency_fix_hashes.php — Could ALTER/DROP tables - fix_perceptual_hash.php — Could ALTER table columns - fix_hash_columns.php — Could ALTER table columns - fix_hash_to_binary.php — Could ALTER table columns - create_comments_table.php — Could CREATE/ALTER tables - add-location.php — Could ALTER table columns - add_duplicate_column.php — Could ALTER table columns (had weak IP check) - install-fix-column-sizes.php — Could ALTER table columns - diagnose-physical-fields.php — Exposed database structure - aireviewinstall.php — Could CREATE tables (had weak localhost check) - addquestions.php — Could CREATE/INSERT into FAQ tables - import.php — Could execute arbitrary SQL from schema files - googleinstall.php — Could modify site settings ================================================================================ INFORMATION DISCLOSURE FIXES ================================================================================ [FIX 11] XSS + INFO DISCLOSURE — debug-rewrite.php Severity: MEDIUM Issue: Echoed $_SERVER['REQUEST_URI'], $_SERVER['QUERY_STRING'], and json_encode($_GET) without any escaping or authentication. Fix applied: - Added admin auth guard - Wrapped all output in htmlspecialchars() [FIX 12] INFO DISCLOSURE — debug_gender.php Severity: MEDIUM Issue: Exposed full database structure and photo/user statistics without any authentication check. Fix applied: Added admin auth guard. [FIX 13] ERROR MESSAGE LEAKS — Multiple public API endpoints Severity: MEDIUM Issue: Exception messages ($e->getMessage()) exposed to public users, potentially leaking database structure, file paths, and query details. Files fixed: - api/search-users.php — Now returns generic "Search failed" - api/categories.php — Now returns generic "Failed to load categories" - api/block-user.php — Now returns generic "An error occurred" - api/ai-rating-upload.php — Removed DB error details (2 locations) - api/bugs.php — Now returns generic error, logs to error_log() ================================================================================ CSRF TIMING ATTACK FIX ================================================================================ [FIX 14] CSRF TOKEN COMPARISON — api/delete-account.php Severity: LOW Issue: Used !== (string comparison) instead of hash_equals() for CSRF token validation, vulnerable to timing attacks. Fix applied: Changed to hash_equals() for constant-time comparison. ================================================================================ PREVIOUSLY SECURE (CONFIRMED) ================================================================================ The following areas were audited and found to be properly secured: - All main API endpoints (rate.php, upload.php, guest-upload.php, etc.) use prepared statements with parameter binding correctly. - Password hashing uses PASSWORD_ARGON2ID (in User.php). - File uploads validate MIME types via finfo_file() and use random filenames. - Command execution uses escapeshellarg() (in ai-rating-upload.php). - Session management uses database-backed sessions with expiry. - Admin panel (admin/) uses auth_check.php on all pages. - XSS protection: Consistent htmlspecialchars() usage across output. - CSRF tokens with hash_equals() on upload and sensitive forms. - No eval(), assert(), or dynamic includes with user input found. - No open redirect vulnerabilities found. - database/ directory already had IP-restricted .htaccess (87.208.58.188). ================================================================================ RECOMMENDATIONS FOR FURTHER HARDENING ================================================================================ 1. RATE LIMITING: Implement rate limiting on login.php, register.php, and forgotpassword.php to prevent brute force attacks. Consider a middleware that tracks failed attempts per IP/username in the database. 2. DELETE INSTALL SCRIPTS: After all migrations are complete, remove all install-*.php, fix-*.php, emergency-*.php from the public_html root. They should only exist temporarily during deployment. 3. MOVE ADMIN DATABASE TOOLS: Consider moving remaining database tools into admin/database/ which already has IP-restricted .htaccess. 4. WAF / STATIC ANALYSIS: Consider running PHPStan or Psalm with SQL injection detection rules as part of any deployment process. 5. REVIEW DATABASE USER PRIVILEGES: The database user used by the application should NOT have DROP TABLE, DROP DATABASE, or ALTER TABLE permissions in production. Only a separate migration user should have these privileges. This would have prevented the table-dropping attack even if SQL injection succeeded. 6. BACKUP STRATEGY: Ensure automated daily database backups are in place to recover from future incidents quickly. ================================================================================ FILES MODIFIED IN THIS UPDATE ================================================================================ api/search-users.php — SQLi fix (LIMIT) + error leak fix api/chat-messages.php — SQLi fix (LIMIT) api/categories.php — Error message leak fix api/block-user.php — Error message leak fix api/ai-rating-upload.php — Error message leak fix (2 locations) api/bugs.php — Error message leak fix api/delete-account.php — CSRF timing attack fix admin/cam-sessions.php — SQLi fix (WHERE + whitelist) admin/comments.php — SQLi fix (WHERE + LIMIT + bulk actions) admin/staff-ratings.php — SQLi fix (LIMIT/OFFSET) admin/celebration-pics.php — SQLi fix (LIMIT/OFFSET) admin/photo-mod-applications.php — SQLi fix (stats queries + LIMIT + whitelist) admin/ai-activity.php — SQLi fix (LIKE wildcard escaping) .htaccess — Blocked 50+ install/fix/debug scripts emergency_fix_hashes.php — Added admin auth guard fix_perceptual_hash.php — Added admin auth guard fix_hash_columns.php — Added admin auth guard fix_hash_to_binary.php — Added admin auth guard create_comments_table.php — Added admin auth guard add-location.php — Added admin auth guard add_duplicate_column.php — Added admin auth guard install-fix-column-sizes.php — Added admin auth guard diagnose-physical-fields.php — Added admin auth guard aireviewinstall.php — Replaced weak localhost check with admin auth addquestions.php — Added admin auth guard import.php — Added admin auth guard googleinstall.php — Added admin auth guard debug-rewrite.php — Added admin auth + XSS fix debug_gender.php — Added admin auth guard ================================================================================ END OF SECURITY AUDIT LOG ================================================================================ ================================================================================ FOLLOW-UP: FILE CLEANUP — April 15, 2026 ================================================================================ Deleted 137 unnecessary install/migration/debug/fix scripts: - 77 from public_html root (install-*, fix-*, debug-*, emergency-*, etc.) - 58 from database/ (install_*, add_*, migrate_*, run_*, etc.) - 2 from games/ (install-truthordare.php, install-leaderboard.php) These scripts are no longer needed — all migrations have been applied. The .htaccess rules blocking them remain as defense-in-depth. ================================================================================ COMPREHENSIVE SECURITY HARDENING — April 15, 2026 ================================================================================ --- SQL INJECTION: LIMIT/OFFSET Parameterization (20 files) --- All remaining SQL queries with interpolated LIMIT/OFFSET values have been converted to use prepared statement placeholders (?): Public-facing: - sharedairatings.php, sharedstaffairating.php, QuestionHistory.php - banlist.php, buglist.php Admin panel: - admin/aistorylist.php, admin/safeguard-tests.php, admin/bugs.php - admin/bans.php (2 queries: appeals + bans tabs) - admin/video-ai-activity.php (query + exec->prepare for prune) - admin/ai-activity.php, admin/check-ass-log.php - admin/legal-privacy.php, admin/legal-terms.php - admin/job-applications.php, admin/volunteer-applications.php - admin/videos.php, admin/guest-sessions.php - admin/users.php, admin/missing-data.php --- SESSION HARDENING --- - config/config.php: Session cookie 'secure' flag set to true (HTTPS only) - classes/User.php: Added session_regenerate_id(true) after login --- FILE UPLOAD SECURITY --- - uploads/.htaccess: Created - blocks PHP execution in uploads directory - api/upload-avatar.php + admin/api/upload-avatar.php: Extension whitelist - api/upload-avatar.php: Added CSRF token validation --- CSRF PROTECTION --- - includes/profile/settings_tab.php: !== changed to hash_equals() - admin/includes/profile/settings_tab.php: Same fix - api/wipe-ai-memory.php: Same fix --- OPEN REDIRECT PREVENTION --- - api/rate.php, api/rate-video.php: HTTP_REFERER host validation - admin/api/rate.php, admin/api/rate-video.php: Same fix - admin/tickets.php: Whitelisted GET params instead of raw QUERY_STRING --- ERROR MESSAGE DISCLOSURE (14 files) --- Replaced $e->getMessage() in user-facing output with generic messages: api/admin-give-points.php, api/boost-photo.php, admin/api/admin-give-points.php, admin/api/block-user.php, admin/ai-activity.php, delete-photo.php, delete-video.php, api/guest-review-request.php, api/guest-edit-photo.php, api/guest-rotate-photo.php, api/rate.php, admin/api/rate.php, admin/api/rate-video.php --- DEBUG DATA REMOVAL --- - api/rate-video.php: Removed error_log() of raw POST data --- SECURITY HEADERS (.htaccess) --- X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN, X-XSS-Protection: 1; mode=block, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy: camera=(), microphone=(), geolocation=(), Content-Security-Policy: default-src 'self' with proper source whitelisting --- ERROR DISPLAY --- - config/config.php: display_errors=0, log_errors=1 - .user.ini + logs/.htaccess created for defense-in-depth --- RECOMMENDATIONS --- 1. Revoke DROP TABLE / DROP DATABASE from MySQL app user 2. Uncomment Force HTTPS rules in .htaccess when SSL is active 3. Add rate limiting on login/registration/rating endpoints 4. Consider WAF (Cloudflare, ModSecurity) 5. Establish regular off-server database backups ================================================================================ ================================================================================ SECURITY ALERTS SYSTEM — April 15, 2026 ================================================================================ Created a real-time security alerting system that logs attack attempts to the admin panel. Alerts are generated automatically when suspicious activity is detected by the request scanner or by individual security hooks. --- NEW FILES --- - classes/SecurityAlert.php: Core alert logging class • Severity levels: CRITICAL, HIGH, MEDIUM, LOW • Categories: sql_injection, csrf_failure, auth_bypass, malicious_upload, open_redirect, path_traversal, rate_limit, blocked_file_access, xss_attempt • Auto-creates security_alerts table on first use • Methods: log(), sqlInjection(), csrfFailure(), authBypass(), maliciousUpload(), openRedirect(), blockedAccess(), getUnreadCount() - includes/request_scanner.php: Automatic request-level attack detection • Scans all GET/POST params + QUERY_STRING on every request • Detects: SQL injection patterns, XSS, path traversal • Loaded via config/config.php on every page load - admin/security-alerts.php: Admin dashboard for viewing/managing alerts • Stats grid, top attacking IPs with ban button, category breakdown • Severity/category/search/unread filters • Mark read, delete, ban IP actions (CSRF-protected) --- HOOKS ADDED --- - config/config.php: Loads request_scanner.php after autoloader - admin/auth_check.php: Logs auth bypass attempts (unauthorized admin access) - upload-avatar.php: Logs CSRF failures + malicious file upload attempts - api/wipe-ai-memory.php: Logs CSRF failures - includes/profile/settings_tab.php: Logs CSRF failures - api/rate.php, api/rate-video.php: Logs open redirect attempts - adminsidebar.php + admin/adminsidebar.php: Unread alert count badge + nav link ================================================================================ RATE LIMITING & ADMIN LOGIN LOGGING — April 15, 2026 ================================================================================ Implemented brute-force protection and admin login audit trail. --- NEW FILES --- - classes/RateLimiter.php: Rate limiting middleware • Tracks failed attempts per IP + username in rate_limit_attempts table • Configurable thresholds per action type • Auto-clears failed attempts on successful login • Logs SecurityAlert on 3+ failed attempts (escalating severity) • Also manages admin_login_log table for audit trail • logAdminLogin() logs both successes and failures with IP + User-Agent • cleanup() method for cron-based old record purging - admin/admin-login-log.php: Admin login audit dashboard • 24h stats: successful logins, failed attempts, unique failed IPs • Filter by success/failed, IP address • Full table with timestamp, status, username, user ID, IP, reason, UA • Pagination support - admin/db-privileges.php: Database privilege lockdown guide • Shows current DB config (host, name, user) • Generates SQL commands to create restricted app user • Step-by-step instructions for phpMyAdmin or CLI • Copy-to-clipboard functionality • Explains privilege meanings and shared hosting alternatives --- MODIFIED FILES --- - login.php: Rate limiting hooked in (5 failed attempts / 15 min window) • Checks rate limit before attempting login • Records success/failure after attempt • Logs admin logins (success + failure) to admin_login_log • Failed admin login checks account_level and logs separately • Shows "Too many failed attempts" with retry countdown - api/register.php: Rate limiting (3 registrations / hour / IP) • Checks before processing registration • Records success/failure - forgotpassword.php: Rate limiting (3 requests / hour / IP) • Checks before sending reset email • Records attempt on submission --- ADMIN SIDEBAR LINKS ADDED --- - admin/adminsidebar.php: Added "🔐 Admin Login Log" + "🔒 DB Privileges" - adminsidebar.php (root): Same links added under System dropdown --- DATABASE DIRECTORY CLEANUP --- - Deleted root database/ directory (61 SQL/MD files) - All files confirmed to be duplicates already in admin/database/ - admin/database/ retained with IP-restricted .htaccess --- UPDATED RECOMMENDATIONS --- 1. ✅ Rate limiting implemented (was #3 recommendation) 2. ❌ DB privilege lockdown — NOT POSSIBLE on current shared hosting (CREATE USER denied; provider does not allow it) Mitigated by: all SQL is now parameterized, so injection can't execute DDL 3. ⬜ Enable HTTPS force-redirect in .htaccess when SSL is active 4. ⬜ Consider WAF (Cloudflare, ModSecurity) 5. ⬜ Establish regular off-server database backups 6. NEW: Set up cron job to call RateLimiter::cleanup() daily Script: cron-cleanup-rate-limits.php Cron command (run daily at 3:00 AM): 0 3 * * * /usr/bin/php /home/flipkabouter/domains/ratemybody.net/public_html/cron-cleanup-rate-limits.php >> /home/flipkabouter/domains/ratemybody.net/public_html/logs/cron-cleanup.log 2>&1 ================================================================================