![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/soundstudiopro.com/private_html/ |
<?php
/**
* SQL Injection Security Scanner
* Scans all PHP files for potential SQL injection vulnerabilities
*
* Usage: php security_scan_sql.php
*/
error_reporting(E_ALL);
ini_set('display_errors', 1);
class SQLInjectionScanner {
private $vulnerabilities = [];
private $scannedFiles = 0;
private $basePath;
public function __construct($basePath) {
$this->basePath = rtrim($basePath, '/');
}
/**
* Scan all PHP files in the directory
*/
public function scan() {
echo "š Starting SQL Injection Security Scan...\n\n";
echo "Scanning directory: {$this->basePath}\n";
echo str_repeat("=", 80) . "\n\n";
$this->scanDirectory($this->basePath);
$this->generateReport();
}
/**
* Recursively scan directory
*/
private function scanDirectory($dir) {
$files = glob($dir . '/*.php');
foreach ($files as $file) {
// Skip backup files and this scanner itself
if (strpos($file, '.backup') !== false ||
strpos($file, 'security_scan_sql.php') !== false ||
strpos($file, 'vendor/') !== false ||
strpos($file, 'node_modules/') !== false) {
continue;
}
$this->scanFile($file);
}
// Scan subdirectories
$dirs = glob($dir . '/*', GLOB_ONLYDIR);
foreach ($dirs as $subdir) {
// Skip common directories that don't need scanning
if (strpos($subdir, 'vendor') !== false ||
strpos($subdir, 'node_modules') !== false ||
strpos($subdir, '.git') !== false) {
continue;
}
$this->scanDirectory($subdir);
}
}
/**
* Scan a single PHP file
*/
private function scanFile($filePath) {
$this->scannedFiles++;
$relativePath = str_replace($this->basePath . '/', '', $filePath);
$content = file_get_contents($filePath);
$lines = explode("\n", $content);
// Pattern 1: LIMIT/OFFSET with direct variable insertion
if (preg_match('/LIMIT\s+\$|OFFSET\s+\$/', $content)) {
$this->checkLimitOffset($filePath, $relativePath, $lines);
}
// Pattern 2: SQL queries with direct variable concatenation
if (preg_match('/->(query|exec)\([^)]*\$/', $content)) {
$this->checkDirectQuery($filePath, $relativePath, $lines);
}
// Pattern 3: SQL string concatenation with user input
if (preg_match('/["\'].*\$_(GET|POST|REQUEST|COOKIE|SERVER).*["\']/', $content)) {
$this->checkStringConcatenation($filePath, $relativePath, $lines);
}
// Pattern 4: WHERE/ORDER BY with direct variable insertion
if (preg_match('/WHERE.*\$[a-zA-Z_]+|ORDER\s+BY.*\$[a-zA-Z_]+/', $content)) {
$this->checkWhereOrderBy($filePath, $relativePath, $lines);
}
// Pattern 5: Missing prepared statements in common patterns
$this->checkMissingPreparedStatements($filePath, $relativePath, $lines);
}
/**
* Check for LIMIT/OFFSET vulnerabilities
*/
private function checkLimitOffset($filePath, $relativePath, $lines) {
foreach ($lines as $lineNum => $line) {
if (preg_match('/LIMIT\s+(\$[a-zA-Z_]+)\s+OFFSET\s+(\$[a-zA-Z_]+)/', $line, $matches)) {
// Check if it's using prepared statements
$context = $this->getContext($lines, $lineNum, 20);
if (!preg_match('/LIMIT\s+\?|OFFSET\s+\?/', $context)) {
$this->addVulnerability($relativePath, $lineNum + 1,
"LIMIT/OFFSET with direct variable insertion",
$line,
"Use prepared statement parameters: LIMIT ? OFFSET ?"
);
}
} elseif (preg_match('/LIMIT\s+\$[a-zA-Z_]+|OFFSET\s+\$[a-zA-Z_]+/', $line)) {
$context = $this->getContext($lines, $lineNum, 20);
if (!preg_match('/LIMIT\s+\?|OFFSET\s+\?/', $context)) {
$this->addVulnerability($relativePath, $lineNum + 1,
"LIMIT/OFFSET with direct variable insertion",
$line,
"Use prepared statement parameters: LIMIT ? OFFSET ?"
);
}
}
}
}
/**
* Check for direct query() or exec() calls with variables
*/
private function checkDirectQuery($filePath, $relativePath, $lines) {
foreach ($lines as $lineNum => $line) {
// Skip safe patterns (like SHOW TABLES, DESCRIBE, etc.)
if (preg_match('/->(query|exec)\s*\(\s*["\'](SHOW|DESCRIBE|SELECT\s+1|SELECT\s+\*)/i', $line)) {
continue;
}
if (preg_match('/->(query|exec)\s*\(\s*["\'].*\$/', $line)) {
$context = $this->getContext($lines, $lineNum, 10);
// Check if it's a safe static query
if (!preg_match('/["\'](SHOW|DESCRIBE|SELECT\s+1|SELECT\s+\*)/i', $line)) {
$this->addVulnerability($relativePath, $lineNum + 1,
"Direct query()/exec() with variable in SQL string",
$line,
"Use prepared statements with ->prepare() and ->execute()"
);
}
}
}
}
/**
* Check for string concatenation with user input
*/
private function checkStringConcatenation($filePath, $relativePath, $lines) {
foreach ($lines as $lineNum => $line) {
// Look for SQL string building with $_GET, $_POST, etc.
if (preg_match('/["\'].*\$\_(GET|POST|REQUEST|COOKIE).*["\'].*\.\s*["\']|["\'].*\.\s*\$\_(GET|POST|REQUEST|COOKIE)/', $line)) {
$context = $this->getContext($lines, $lineNum, 15);
// Check if it's in a SQL context
if (preg_match('/(SELECT|INSERT|UPDATE|DELETE|WHERE|FROM|ORDER\s+BY|GROUP\s+BY)/i', $context)) {
$this->addVulnerability($relativePath, $lineNum + 1,
"String concatenation with user input in SQL context",
$line,
"Use prepared statements with parameter binding"
);
}
}
}
}
/**
* Check for WHERE/ORDER BY with direct variables
*/
private function checkWhereOrderBy($filePath, $relativePath, $lines) {
foreach ($lines as $lineNum => $line) {
// Check for WHERE with variables (but allow validated switch statements)
if (preg_match('/WHERE.*\$[a-zA-Z_]+(?!\s*==|\s*===|\s*!=|\s*!==)/', $line) &&
!preg_match('/switch\s*\(|case\s+/', $this->getContext($lines, $lineNum, 30))) {
$this->addVulnerability($relativePath, $lineNum + 1,
"WHERE clause with direct variable (may be unsafe)",
$line,
"Verify variable is validated or use prepared statement parameters"
);
}
}
}
/**
* Check for missing prepared statements in common patterns
*/
private function checkMissingPreparedStatements($filePath, $relativePath, $lines) {
$inQuery = false;
$queryStart = 0;
$hasPrepare = false;
foreach ($lines as $lineNum => $line) {
// Detect SQL query start
if (preg_match('/->prepare\s*\(/', $line)) {
$hasPrepare = true;
}
// Check for execute without prepare
if (preg_match('/->execute\s*\(/', $line) && !$hasPrepare) {
$context = $this->getContext($lines, $lineNum, 30);
if (preg_match('/(SELECT|INSERT|UPDATE|DELETE)/i', $context)) {
$this->addVulnerability($relativePath, $lineNum + 1,
"execute() called without prepare()",
$line,
"Always use ->prepare() before ->execute()"
);
}
}
// Reset hasPrepare after execute
if (preg_match('/->execute\s*\(/', $line)) {
$hasPrepare = false;
}
}
}
/**
* Get context around a line
*/
private function getContext($lines, $lineNum, $contextLines = 10) {
$start = max(0, $lineNum - $contextLines);
$end = min(count($lines), $lineNum + $contextLines + 1);
return implode("\n", array_slice($lines, $start, $end - $start));
}
/**
* Add vulnerability to report
*/
private function addVulnerability($file, $line, $type, $code, $recommendation) {
$this->vulnerabilities[] = [
'file' => $file,
'line' => $line,
'type' => $type,
'code' => trim($code),
'recommendation' => $recommendation
];
}
/**
* Generate security report
*/
private function generateReport() {
echo "\n" . str_repeat("=", 80) . "\n";
echo "š SCAN RESULTS\n";
echo str_repeat("=", 80) . "\n\n";
echo "Files scanned: {$this->scannedFiles}\n";
echo "Vulnerabilities found: " . count($this->vulnerabilities) . "\n\n";
if (empty($this->vulnerabilities)) {
echo "ā
No SQL injection vulnerabilities detected!\n";
echo "All queries appear to be using prepared statements correctly.\n\n";
} else {
echo "ā ļø VULNERABILITIES FOUND:\n\n";
// Group by file
$grouped = [];
foreach ($this->vulnerabilities as $vuln) {
$grouped[$vuln['file']][] = $vuln;
}
foreach ($grouped as $file => $vulns) {
echo "š File: {$file}\n";
echo str_repeat("-", 80) . "\n";
foreach ($vulns as $vuln) {
echo " Line {$vuln['line']}: {$vuln['type']}\n";
echo " Code: {$vuln['code']}\n";
echo " Fix: {$vuln['recommendation']}\n";
echo "\n";
}
}
// Generate summary
echo "\n" . str_repeat("=", 80) . "\n";
echo "š SUMMARY BY TYPE\n";
echo str_repeat("=", 80) . "\n\n";
$byType = [];
foreach ($this->vulnerabilities as $vuln) {
$byType[$vuln['type']] = ($byType[$vuln['type']] ?? 0) + 1;
}
foreach ($byType as $type => $count) {
echo " {$type}: {$count}\n";
}
}
// Save report to file
$reportFile = $this->basePath . '/security_scan_report_' . date('Y-m-d_His') . '.txt';
$report = $this->generateTextReport();
file_put_contents($reportFile, $report);
echo "\nš Full report saved to: {$reportFile}\n";
}
/**
* Generate text report
*/
private function generateTextReport() {
$report = "SQL Injection Security Scan Report\n";
$report .= "Generated: " . date('Y-m-d H:i:s') . "\n";
$report .= str_repeat("=", 80) . "\n\n";
$report .= "Files scanned: {$this->scannedFiles}\n";
$report .= "Vulnerabilities found: " . count($this->vulnerabilities) . "\n\n";
if (!empty($this->vulnerabilities)) {
$grouped = [];
foreach ($this->vulnerabilities as $vuln) {
$grouped[$vuln['file']][] = $vuln;
}
foreach ($grouped as $file => $vulns) {
$report .= "File: {$file}\n";
$report .= str_repeat("-", 80) . "\n";
foreach ($vulns as $vuln) {
$report .= " Line {$vuln['line']}: {$vuln['type']}\n";
$report .= " Code: {$vuln['code']}\n";
$report .= " Fix: {$vuln['recommendation']}\n\n";
}
}
}
return $report;
}
}
// Run the scanner
$basePath = __DIR__;
$scanner = new SQLInjectionScanner($basePath);
$scanner->scan();
echo "\nā
Scan complete!\n";