Skip to main content

Course Progress

Loading...

Prepared Statements and Security Concerns

Duration: 90 minutes
Lesson 4 of 8

Learning Objectives

  • Understand SQL injection attacks and their impact
  • Master prepared statements in both PDO and MySQLi
  • Implement parameter binding correctly
  • Apply input validation and sanitization
  • Use whitelisting for dynamic queries
  • Implement defense-in-depth security strategies
  • Handle different data types securely
  • Audit and test for security vulnerabilities

Understanding SQL Injection

SQL injection is one of the most dangerous web vulnerabilities. Let's understand how it works and how to prevent it completely! 🛡️

⚠️
SQL Injection Risks
SQL injection can lead to:
  • Data Breach:Attackers can extract sensitive information
  • Data Manipulation:Modify or delete database records
  • Authentication Bypass:Login without credentials
  • Remote Code Execution:Execute system commands
  • Complete Takeover:Gain administrative access

Prepared Statements Deep Dive

graph TB INPUT[User Input] --> PREPARE[Prepare Statement] PREPARE --> COMPILE[SQL Compiled] COMPILE --> PLAN[Execution Plan Created] INPUT --> BIND[Bind Parameters] BIND --> EXECUTE[Execute Statement] PLAN --> EXECUTE EXECUTE --> SAFE[Safe Execution] subgraph "Protection Layer" BIND --> |"Data separated from code"| SAFE end style INPUT fill:#fee2e2 style PREPARE fill:#dcfce7 style SAFE fill:#dbeafe

Complete Prepared Statements Implementation

<?php
// ========================================
// PDO PREPARED STATEMENTS
// ========================================

class SecurePDODatabase {
    private $pdo;
    private $allowedTables = ['users', 'products', 'orders'];
    private $allowedColumns = [];
    
    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
        $this->initializeAllowedColumns();
    }
    
    /**
     * Initialize allowed columns for each table
     */
    private function initializeAllowedColumns() {
        $this->allowedColumns = [
            'users' => ['id', 'username', 'email', 'status', 'created_at'],
            'products' => ['id', 'name', 'price', 'category', 'stock'],
            'orders' => ['id', 'user_id', 'total', 'status', 'created_at']
        ];
    }
    
    /**
     * Secure SELECT with prepared statements
     */
    public function secureSelect($table, $conditions = [], $columns = ['*']) {
        // Validate table name (whitelist)
        if (!in_array($table, $this->allowedTables)) {
            throw new Exception("Invalid table name");
        }
        
        // Validate columns (whitelist)
        $safeColumns = $this->validateColumns($table, $columns);
        
        // Build query
        $sql = "SELECT " . implode(', ', $safeColumns) . " FROM $table";
        
        // Add WHERE conditions
        if (!empty($conditions)) {
            $whereClauses = [];
            $params = [];
            
            foreach ($conditions as $column => $value) {
                // Validate column name
                if (!in_array($column, $this->allowedColumns[$table])) {
                    throw new Exception("Invalid column: $column");
                }
                
                if (is_array($value)) {
                    // Handle IN clause
                    $placeholders = array_fill(0, count($value), '?');
                    $whereClauses[] = "$column IN (" . implode(', ', $placeholders) . ")";
                    $params = array_merge($params, $value);
                } elseif ($value === null) {
                    // Handle NULL
                    $whereClauses[] = "$column IS NULL";
                } else {
                    // Regular condition
                    $whereClauses[] = "$column = ?";
                    $params[] = $value;
                }
            }
            
            $sql .= " WHERE " . implode(' AND ', $whereClauses);
        }
        
        // Prepare and execute
        try {
            $stmt = $this->pdo->prepare($sql);
            $stmt->execute($params);
            
            return $stmt->fetchAll();
            
        } catch (PDOException $e) {
            $this->logSecurityEvent('Query failed', [
                'sql' => $sql,
                'error' => $e->getMessage()
            ]);
            throw new Exception("Database query failed");
        }
    }
    
    /**
     * Secure INSERT with type checking
     */
    public function secureInsert($table, array $data) {
        // Validate table
        if (!in_array($table, $this->allowedTables)) {
            throw new Exception("Invalid table name");
        }
        
        // Validate columns
        $columns = array_keys($data);
        foreach ($columns as $column) {
            if (!in_array($column, $this->allowedColumns[$table])) {
                throw new Exception("Invalid column: $column");
            }
        }
        
        // Build query
        $placeholders = array_fill(0, count($data), '?');
        
        $sql = "INSERT INTO $table (" . implode(', ', $columns) . ") 
                VALUES (" . implode(', ', $placeholders) . ")";
        
        try {
            $stmt = $this->pdo->prepare($sql);
            
            // Bind parameters with type detection
            $i = 1;
            foreach ($data as $value) {
                $type = $this->getPDOType($value);
                $stmt->bindValue($i++, $value, $type);
            }
            
            $stmt->execute();
            
            return [
                'success' => true,
                'id' => $this->pdo->lastInsertId()
            ];
            
        } catch (PDOException $e) {
            $this->logSecurityEvent('Insert failed', [
                'table' => $table,
                'error' => $e->getMessage()
            ]);
            throw new Exception("Insert operation failed");
        }
    }
    
    /**
     * Secure UPDATE with strict validation
     */
    public function secureUpdate($table, array $data, array $where) {
        // Validate table
        if (!in_array($table, $this->allowedTables)) {
            throw new Exception("Invalid table name");
        }
        
        // Validate all columns
        $allColumns = array_merge(array_keys($data), array_keys($where));
        foreach ($allColumns as $column) {
            if (!in_array($column, $this->allowedColumns[$table])) {
                throw new Exception("Invalid column: $column");
            }
        }
        
        // Build SET clause
        $setClauses = [];
        $params = [];
        
        foreach ($data as $column => $value) {
            $setClauses[] = "$column = ?";
            $params[] = $value;
        }
        
        // Build WHERE clause
        $whereClauses = [];
        foreach ($where as $column => $value) {
            $whereClauses[] = "$column = ?";
            $params[] = $value;
        }
        
        $sql = "UPDATE $table SET " . implode(', ', $setClauses) . 
               " WHERE " . implode(' AND ', $whereClauses);
        
        try {
            $stmt = $this->pdo->prepare($sql);
            $stmt->execute($params);
            
            return [
                'success' => true,
                'affected' => $stmt->rowCount()
            ];
            
        } catch (PDOException $e) {
            $this->logSecurityEvent('Update failed', [
                'table' => $table,
                'error' => $e->getMessage()
            ]);
            throw new Exception("Update operation failed");
        }
    }
    
    /**
     * Secure DELETE with confirmation
     */
    public function secureDelete($table, array $where, $requireConfirmation = true) {
        // Validate table
        if (!in_array($table, $this->allowedTables)) {
            throw new Exception("Invalid table name");
        }
        
        // Prevent accidental mass deletion
        if (empty($where) && $requireConfirmation) {
            throw new Exception("Cannot delete without WHERE clause");
        }
        
        // Validate columns
        foreach (array_keys($where) as $column) {
            if (!in_array($column, $this->allowedColumns[$table])) {
                throw new Exception("Invalid column: $column");
            }
        }
        
        // Build WHERE clause
        $whereClauses = [];
        $params = [];
        
        foreach ($where as $column => $value) {
            $whereClauses[] = "$column = ?";
            $params[] = $value;
        }
        
        $sql = "DELETE FROM $table WHERE " . implode(' AND ', $whereClauses);
        
        try {
            $stmt = $this->pdo->prepare($sql);
            $stmt->execute($params);
            
            $affected = $stmt->rowCount();
            
            // Log deletion for audit
            $this->logSecurityEvent('Data deleted', [
                'table' => $table,
                'conditions' => $where,
                'affected' => $affected
            ]);
            
            return [
                'success' => true,
                'deleted' => $affected
            ];
            
        } catch (PDOException $e) {
            $this->logSecurityEvent('Delete failed', [
                'table' => $table,
                'error' => $e->getMessage()
            ]);
            throw new Exception("Delete operation failed");
        }
    }
    
    /**
     * Execute stored procedure securely
     */
    public function callProcedure($procedure, array $params = []) {
        // Whitelist procedures
        $allowedProcedures = ['GetUserById', 'UpdateInventory', 'ProcessOrder'];
        
        if (!in_array($procedure, $allowedProcedures)) {
            throw new Exception("Invalid procedure");
        }
        
        $placeholders = array_fill(0, count($params), '?');
        $sql = "CALL $procedure(" . implode(', ', $placeholders) . ")";
        
        try {
            $stmt = $this->pdo->prepare($sql);
            $stmt->execute($params);
            
            return $stmt->fetchAll();
            
        } catch (PDOException $e) {
            $this->logSecurityEvent('Procedure failed', [
                'procedure' => $procedure,
                'error' => $e->getMessage()
            ]);
            throw new Exception("Procedure execution failed");
        }
    }
    
    /**
     * Validate columns against whitelist
     */
    private function validateColumns($table, $columns) {
        if ($columns === ['*']) {
            return $columns;
        }
        
        $validColumns = [];
        foreach ($columns as $column) {
            if (!in_array($column, $this->allowedColumns[$table])) {
                throw new Exception("Invalid column: $column");
            }
            $validColumns[] = $column;
        }
        
        return $validColumns;
    }
    
    /**
     * Get PDO parameter type
     */
    private function getPDOType($value) {
        if (is_null($value)) {
            return PDO::PARAM_NULL;
        } elseif (is_bool($value)) {
            return PDO::PARAM_BOOL;
        } elseif (is_int($value)) {
            return PDO::PARAM_INT;
        } else {
            return PDO::PARAM_STR;
        }
    }
    
    /**
     * Log security events
     */
    private function logSecurityEvent($event, $data) {
        $logEntry = [
            'timestamp' => date('Y-m-d H:i:s'),
            'event' => $event,
            'data' => $data,
            'ip' => $_SERVER['REMOTE_ADDR'] ?? 'CLI',
            'user' => $_SESSION['user_id'] ?? 'anonymous'
        ];
        
        error_log(json_encode($logEntry), 3, '/var/log/security.log');
    }
}

// ========================================
// MYSQLI PREPARED STATEMENTS
// ========================================

class SecureMySQLiDatabase {
    private $mysqli;
    private $typeMap = [];
    
    public function __construct(mysqli $mysqli) {
        $this->mysqli = $mysqli;
    }
    
    /**
     * Secure query with automatic type detection
     */
    public function secureQuery($sql, array $params = []) {
        // Check for dangerous patterns
        if ($this->containsDangerousSQL($sql)) {
            throw new Exception("Potentially dangerous SQL detected");
        }
        
        $stmt = $this->mysqli->prepare($sql);
        
        if (!$stmt) {
            throw new Exception("Prepare failed: " . $this->mysqli->error);
        }
        
        if (!empty($params)) {
            // Build type string
            $types = $this->buildTypeString($params);
            
            // Bind parameters
            $stmt->bind_param($types, ...$params);
        }
        
        if (!$stmt->execute()) {
            throw new Exception("Execute failed: " . $stmt->error);
        }
        
        // Get result if SELECT query
        if (stripos($sql, 'SELECT') === 0) {
            $result = $stmt->get_result();
            $data = [];
            
            while ($row = $result->fetch_assoc()) {
                $data[] = $row;
            }
            
            $stmt->close();
            return $data;
        }
        
        // Return affected rows for other queries
        $affected = $stmt->affected_rows;
        $stmt->close();
        
        return ['affected' => $affected];
    }
    
    /**
     * Build type string for mysqli
     */
    private function buildTypeString(array $params) {
        $types = '';
        
        foreach ($params as $param) {
            if (is_int($param)) {
                $types .= 'i';
            } elseif (is_float($param)) {
                $types .= 'd';
            } elseif (is_string($param)) {
                $types .= 's';
            } else {
                $types .= 'b'; // blob
            }
        }
        
        return $types;
    }
    
    /**
     * Check for dangerous SQL patterns
     */
    private function containsDangerousSQL($sql) {
        $dangerous = [
            '/DROP\s+TABLE/i',
            '/DROP\s+DATABASE/i',
            '/TRUNCATE/i',
            '/DELETE\s+FROM\s+\w+\s*$/i', // DELETE without WHERE
            '/UPDATE\s+\w+\s+SET\s+[^W]+$/i', // UPDATE without WHERE
            '/;.*--;?/i', // Multiple statements
            '/UNION.*SELECT/i', // UNION without proper context
            '/INTO\s+OUTFILE/i', // File operations
            '/LOAD_FILE/i'
        ];
        
        foreach ($dangerous as $pattern) {
            if (preg_match($pattern, $sql)) {
                return true;
            }
        }
        
        return false;
    }
}

// ========================================
// INPUT VALIDATION & SANITIZATION
// ========================================

class InputValidator {
    
    /**
     * Validate and sanitize input
     */
    public static function validate($input, $type, $options = []) {
        switch ($type) {
            case 'int':
                return self::validateInt($input, $options);
                
            case 'float':
                return self::validateFloat($input, $options);
                
            case 'email':
                return self::validateEmail($input);
                
            case 'url':
                return self::validateUrl($input);
                
            case 'alphanumeric':
                return self::validateAlphanumeric($input, $options);
                
            case 'date':
                return self::validateDate($input, $options);
                
            case 'phone':
                return self::validatePhone($input);
                
            case 'uuid':
                return self::validateUuid($input);
                
            default:
                return self::validateString($input, $options);
        }
    }
    
    /**
     * Validate integer
     */
    private static function validateInt($input, $options = []) {
        $filtered = filter_var($input, FILTER_VALIDATE_INT);
        
        if ($filtered === false) {
            throw new ValidationException("Invalid integer");
        }
        
        // Check range
        if (isset($options['min']) && $filtered < $options['min']) {
            throw new ValidationException("Value below minimum");
        }
        
        if (isset($options['max']) && $filtered > $options['max']) {
            throw new ValidationException("Value above maximum");
        }
        
        return $filtered;
    }
    
    /**
     * Validate float
     */
    private static function validateFloat($input, $options = []) {
        $filtered = filter_var($input, FILTER_VALIDATE_FLOAT);
        
        if ($filtered === false) {
            throw new ValidationException("Invalid float");
        }
        
        return $filtered;
    }
    
    /**
     * Validate email
     */
    private static function validateEmail($input) {
        $filtered = filter_var($input, FILTER_VALIDATE_EMAIL);
        
        if ($filtered === false) {
            throw new ValidationException("Invalid email address");
        }
        
        // Additional checks
        if (!checkdnsrr(substr(strrchr($filtered, "@"), 1), 'MX')) {
            throw new ValidationException("Email domain does not exist");
        }
        
        return $filtered;
    }
    
    /**
     * Validate URL
     */
    private static function validateUrl($input) {
        $filtered = filter_var($input, FILTER_VALIDATE_URL);
        
        if ($filtered === false) {
            throw new ValidationException("Invalid URL");
        }
        
        // Check allowed protocols
        $allowedProtocols = ['http', 'https'];
        $protocol = parse_url($filtered, PHP_URL_SCHEME);
        
        if (!in_array($protocol, $allowedProtocols)) {
            throw new ValidationException("Invalid URL protocol");
        }
        
        return $filtered;
    }
    
    /**
     * Validate alphanumeric
     */
    private static function validateAlphanumeric($input, $options = []) {
        $allowSpaces = $options['allow_spaces'] ?? false;
        $pattern = $allowSpaces ? '/^[a-zA-Z0-9\s]+$/' : '/^[a-zA-Z0-9]+$/';
        
        if (!preg_match($pattern, $input)) {
            throw new ValidationException("Invalid alphanumeric value");
        }
        
        return $input;
    }
    
    /**
     * Validate date
     */
    private static function validateDate($input, $options = []) {
        $format = $options['format'] ?? 'Y-m-d';
        $date = DateTime::createFromFormat($format, $input);
        
        if (!$date || $date->format($format) !== $input) {
            throw new ValidationException("Invalid date format");
        }
        
        return $input;
    }
    
    /**
     * Validate phone number
     */
    private static function validatePhone($input) {
        // Remove non-numeric characters
        $phone = preg_replace('/[^0-9]/', '', $input);
        
        // Check length (adjust for your country)
        if (strlen($phone) < 10 || strlen($phone) > 15) {
            throw new ValidationException("Invalid phone number");
        }
        
        return $phone;
    }
    
    /**
     * Validate UUID
     */
    private static function validateUuid($input) {
        $pattern = '/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i';
        
        if (!preg_match($pattern, $input)) {
            throw new ValidationException("Invalid UUID");
        }
        
        return $input;
    }
    
    /**
     * Validate string with sanitization
     */
    private static function validateString($input, $options = []) {
        $minLength = $options['min_length'] ?? 0;
        $maxLength = $options['max_length'] ?? PHP_INT_MAX;
        $allowHtml = $options['allow_html'] ?? false;
        
        // Remove null bytes
        $input = str_replace(chr(0), '', $input);
        
        // Trim whitespace
        $input = trim($input);
        
        // Check length
        if (strlen($input) < $minLength) {
            throw new ValidationException("String too short");
        }
        
        if (strlen($input) > $maxLength) {
            throw new ValidationException("String too long");
        }
        
        // Sanitize HTML
        if (!$allowHtml) {
            $input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
        } else {
            // Allow only safe HTML tags
            $allowedTags = '


Security Testing and Auditing

Security Testing Framework

<?php
// ========================================
// SECURITY TESTING SUITE
// ========================================

class SQLInjectionTester {
    private $testPayloads = [
        // Basic injection attempts
        "' OR '1'='1",
        "1' OR '1' = '1",
        "' OR 1=1--",
        "' OR 'a'='a",
        "' OR ''='",
        
        // Union-based
        "' UNION SELECT * FROM users--",
        "1 UNION SELECT null, version()--",
        
        // Boolean-based
        "1' AND '1'='1",
        "1' AND '1'='2",
        
        // Time-based
        "1' AND SLEEP(5)--",
        "1'; WAITFOR DELAY '0:0:5'--",
        
        // Error-based
        "1'",
        "\\",
        "'\"",
        
        // Stacked queries
        "1'; DROP TABLE users--",
        "1'; DELETE FROM users--",
        
        // Out-of-band
        "1' AND LOAD_FILE('/etc/passwd')--",
        
        // Second-order
        "admin'--",
        
        // NoSQL injection (for MongoDB)
        '{"$ne": ""}',
        '{"$gt": ""}',
        
        // XML injection
        "' or count(//user[userid=$userid])>0 or 'a'='b"
    ];
    
    /**
     * Test function for SQL injection vulnerabilities
     */
    public function testFunction(callable $function, $paramName = 'input') {
        $results = [];
        
        foreach ($this->testPayloads as $payload) {
            try {
                // Test the function with payload
                $result = $function([$paramName => $payload]);
                
                // Analyze result
                $analysis = $this->analyzeResult($result, $payload);
                
                $results[] = [
                    'payload' => $payload,
                    'vulnerable' => $analysis['vulnerable'],
                    'reason' => $analysis['reason']
                ];
                
            } catch (Exception $e) {
                // Error might indicate protection
                $results[] = [
                    'payload' => $payload,
                    'vulnerable' => false,
                    'reason' => 'Exception thrown (likely protected)'
                ];
            }
        }
        
        return $results;
    }
    
    /**
     * Analyze result for signs of injection
     */
    private function analyzeResult($result, $payload) {
        // Check for unexpected data volume
        if (is_array($result) && count($result) > 100) {
            return [
                'vulnerable' => true,
                'reason' => 'Returned excessive data'
            ];
        }
        
        // Check for database errors in result
        if (is_string($result)) {
            $errorPatterns = [
                '/SQL syntax/',
                '/mysql_fetch/',
                '/Warning.*mysql/',
                '/mysqli::/',
                '/PDO::/',
                '/PostgreSQL/',
                '/valid MySQL result/',
                '/mssql_/',
                '/Microsoft.*ODBC.*SQL/',
                '/Oracle.*error/',
                '/SQLite.*error/'
            ];
            
            foreach ($errorPatterns as $pattern) {
                if (preg_match($pattern, $result)) {
                    return [
                        'vulnerable' => true,
                        'reason' => 'Database error exposed'
                    ];
                }
            }
        }
        
        // Check for successful bypass indicators
        if (strpos($payload, 'OR') !== false && !empty($result)) {
            return [
                'vulnerable' => 'possible',
                'reason' => 'OR condition might have bypassed filter'
            ];
        }
        
        return [
            'vulnerable' => false,
            'reason' => 'No injection detected'
        ];
    }
    
    /**
     * Generate security report
     */
    public function generateReport($results) {
        $vulnerable = 0;
        $protected = 0;
        $warnings = [];
        
        foreach ($results as $result) {
            if ($result['vulnerable'] === true) {
                $vulnerable++;
                $warnings[] = "CRITICAL: Vulnerable to: {$result['payload']}";
            } elseif ($result['vulnerable'] === 'possible') {
                $warnings[] = "WARNING: Possible vulnerability: {$result['payload']}";
            } else {
                $protected++;
            }
        }
        
        return [
            'total_tests' => count($results),
            'vulnerabilities' => $vulnerable,
            'protected' => $protected,
            'security_score' => round(($protected / count($results)) * 100, 2),
            'warnings' => $warnings,
            'recommendation' => $vulnerable > 0 ? 
                'URGENT: Fix SQL injection vulnerabilities immediately!' : 
                'Good protection detected, continue monitoring'
        ];
    }
}

// ========================================
// SECURITY AUDIT LOGGER
// ========================================

class SecurityAuditLogger {
    private $logFile;
    
    public function __construct($logFile = '/var/log/security_audit.log') {
        $this->logFile = $logFile;
    }
    
    /**
     * Log query execution
     */
    public function logQuery($sql, $params, $user, $result) {
        $entry = [
            'timestamp' => date('Y-m-d H:i:s'),
            'user' => $user,
            'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            'sql' => $sql,
            'params' => $params,
            'result' => $result ? 'success' : 'failed',
            'checksum' => $this->generateChecksum($sql, $params)
        ];
        
        $this->writeLog($entry);
    }
    
    /**
     * Log suspicious activity
     */
    public function logSuspiciousActivity($activity, $details) {
        $entry = [
            'timestamp' => date('Y-m-d H:i:s'),
            'type' => 'SUSPICIOUS',
            'activity' => $activity,
            'details' => $details,
            'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
            'alert_level' => $this->determineAlertLevel($activity)
        ];
        
        $this->writeLog($entry);
        
        // Send immediate alert for critical issues
        if ($entry['alert_level'] === 'CRITICAL') {
            $this->sendAlert($entry);
        }
    }
    
    /**
     * Determine alert level
     */
    private function determineAlertLevel($activity) {
        $criticalPatterns = [
            'SQL injection attempt',
            'Mass deletion attempt',
            'Privilege escalation',
            'Data exfiltration'
        ];
        
        foreach ($criticalPatterns as $pattern) {
            if (stripos($activity, $pattern) !== false) {
                return 'CRITICAL';
            }
        }
        
        return 'WARNING';
    }
    
    /**
     * Generate checksum for integrity
     */
    private function generateChecksum($sql, $params) {
        return hash('sha256', $sql . serialize($params));
    }
    
    /**
     * Write to log file
     */
    private function writeLog($entry) {
        $json = json_encode($entry) . PHP_EOL;
        file_put_contents($this->logFile, $json, FILE_APPEND | LOCK_EX);
    }
    
    /**
     * Send security alert
     */
    private function sendAlert($entry) {
        // Email alert
        $to = 'security@example.com';
        $subject = 'CRITICAL Security Alert';
        $message = "Critical security event detected:\n\n" . print_r($entry, true);
        
        mail($to, $subject, $message);
        
        // You could also send to:
        // - Slack webhook
        // - SMS via Twilio
        // - PagerDuty
        // - Security Information and Event Management (SIEM) system
    }
    
    /**
     * Analyze logs for patterns
     */
    public function analyzeLogs($hours = 24) {
        $logs = file($this->logFile, FILE_IGNORE_NEW_LINES);
        $cutoff = time() - ($hours * 3600);
        
        $analysis = [
            'failed_queries' => 0,
            'suspicious_activities' => 0,
            'unique_ips' => [],
            'attack_patterns' => []
        ];
        
        foreach ($logs as $log) {
            $entry = json_decode($log, true);
            
            if (strtotime($entry['timestamp']) < $cutoff) {
                continue;
            }
            
            if (isset($entry['result']) && $entry['result'] === 'failed') {
                $analysis['failed_queries']++;
            }
            
            if (isset($entry['type']) && $entry['type'] === 'SUSPICIOUS') {
                $analysis['suspicious_activities']++;
                $analysis['attack_patterns'][] = $entry['activity'];
            }
            
            if (isset($entry['ip'])) {
                $analysis['unique_ips'][$entry['ip']] = 
                    ($analysis['unique_ips'][$entry['ip']] ?? 0) + 1;
            }
        }
        
        return $analysis;
    }
}

// ========================================
// USAGE EXAMPLES
// ========================================

// Test a function for SQL injection
$tester = new SQLInjectionTester();

// Example vulnerable function (DO NOT USE IN PRODUCTION)
$vulnerableFunction = function($params) use ($pdo) {
    $id = $params['input'];
    $sql = "SELECT * FROM users WHERE id = $id"; // VULNERABLE!
    return $pdo->query($sql)->fetchAll();
};

// Example secure function
$secureFunction = function($params) use ($pdo) {
    $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
    $stmt->execute([$params['input']]);
    return $stmt->fetchAll();
};

// Test and compare
echo "Testing vulnerable function:\n";
$results = $tester->testFunction($vulnerableFunction);
$report = $tester->generateReport($results);
print_r($report);

echo "\nTesting secure function:\n";
$results = $tester->testFunction($secureFunction);
$report = $tester->generateReport($results);
print_r($report);

// Security audit logging
$auditLogger = new SecurityAuditLogger();

// Log queries
$auditLogger->logQuery(
    "SELECT * FROM users WHERE id = ?",
    [123],
    $_SESSION['username'] ?? 'anonymous',
    true
);

// Log suspicious activity
$auditLogger->logSuspiciousActivity(
    "SQL injection attempt detected",
    [
        'query' => $_GET['search'] ?? '',
        'pattern_matched' => "' OR '1'='1"
    ]
);

// Analyze logs
$analysis = $auditLogger->analyzeLogs(24);
echo "Security analysis for last 24 hours:\n";
print_r($analysis);

Practice Exercise

💻
Security Implementation Challenges
Complete these security tasks:
  1. Convert vulnerable code to use prepared statements
  2. Implement input validation for a user registration form
  3. Create a whitelist system for dynamic table/column names
  4. Build a SQL injection testing suite
  5. Implement query logging and auditing
  6. Create a security monitoring dashboard
  7. Build rate limiting for database queries
  8. Implement database firewall rules

Additional Resources