Files
xamxam/docs/SECURITY_ANALYSIS.md
2026-02-05 17:37:07 +01:00

6.6 KiB

Security Analysis - Search Feature

Current Security Status

Protections in Place

  1. SQL Injection Prevention

    • Uses PDO prepared statements
    • All parameters bound with bindValue()
    • No direct concatenation of user input into SQL
    • Dynamic WHERE clause built from hardcoded strings only
  2. XSS (Cross-Site Scripting) Prevention

    • All output uses htmlspecialchars()
    • Form values escaped when displayed
    • Search results escaped before rendering
  3. Access Control

    • Only published theses searchable (is_published = 1)
    • Uses read-only view (v_theses_public)
  4. Type Safety

    • Year parameter uses intval()
    • Boolean values properly cast

⚠️ Security Vulnerabilities

1. LIKE Wildcard Injection (Low Severity)

Issue: Users can inject SQL LIKE wildcards (%, _) to match unintended patterns.

Example Attack:

Search query: "%"
Result: Matches ALL theses (bypasses search intent)

Search query: "a%b%c%d%e%f%g%h%i%j%k%l%m%n%o%p%q%r%s%t%u%v%w%x%y%z"
Result: Forces inefficient pattern matching, potential DoS

Current Code:

$bindings[':query'] = '%' . $params['query'] . '%';

Impact:

  • Not SQL injection (still uses prepared statements)
  • Allows overly broad searches
  • Performance degradation with complex patterns
  • Information disclosure through pattern matching

Fix: Escape wildcards before using in LIKE:

private function escapeLikeString($string) {
    return str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $string);
}

// In query:
$bindings[':query'] = '%' . $this->escapeLikeString($params['query']) . '%';

// In SQL:
"title LIKE :query ESCAPE '\\'"

2. No Input Length Validation (Medium Severity)

Issue: No limits on search string length.

Example Attack:

// 10MB query string
$query = str_repeat('a', 10 * 1024 * 1024);

Impact:

  • Memory exhaustion
  • Database query slowdown
  • Denial of Service (DoS)

Fix: Validate input length:

if (strlen($params['query']) > 200) {
    throw new InvalidArgumentException("Search query too long");
}

3. No Rate Limiting (Medium Severity)

Issue: Unlimited search requests allowed.

Example Attack:

# Spam 10,000 requests
for i in {1..10000}; do
    curl "http://site.com/search.php?query=test&page=$i" &
done

Impact:

  • Database overload
  • Server resource exhaustion
  • Denial of Service for legitimate users

Fix: Implement rate limiting (see solution below)


4. No Pagination Limits (Low Severity)

Issue: Users can request excessive offset values.

Example:

search.php?page=999999999

Impact:

  • Database scans large result sets
  • Wasted resources on impossible pages

Fix: Validate pagination:

$limit = max(1, min(100, intval($limit)));  // Max 100 per page
$offset = max(0, intval($offset));

// Optionally limit max offset
if ($offset > 10000) {
    throw new InvalidArgumentException("Page too high");
}

Priority 1: Apply Input Validation (HIGH)

Use the enhanced Database_secure.php class which includes:

  • Wildcard escaping
  • Length validation
  • Range validation
  • ESCAPE clause in LIKE queries

Priority 2: Implement Rate Limiting (MEDIUM)

Example using simple file-based rate limiting:

<?php
// rate_limit.php - Simple rate limiter

function checkRateLimit($identifier, $maxRequests = 10, $timeWindow = 60) {
    $cacheDir = __DIR__ . '/cache/rate_limit';
    if (!is_dir($cacheDir)) {
        mkdir($cacheDir, 0755, true);
    }

    $file = $cacheDir . '/' . md5($identifier) . '.json';

    $data = file_exists($file) ? json_decode(file_get_contents($file), true) : [];

    // Clean old entries
    $now = time();
    $data = array_filter($data, function($timestamp) use ($now, $timeWindow) {
        return ($now - $timestamp) < $timeWindow;
    });

    // Check if limit exceeded
    if (count($data) >= $maxRequests) {
        return false;
    }

    // Add new request
    $data[] = $now;
    file_put_contents($file, json_encode($data));

    return true;
}

// In search.php:
$userIP = $_SERVER['REMOTE_ADDR'];
if (!checkRateLimit($userIP, 20, 60)) { // 20 requests per minute
    http_response_code(429);
    die('Too many requests. Please try again later.');
}

Priority 3: Add Content Security Policy (LOW)

Add to header:

header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' cdn.jsdelivr.net;");
header("X-Content-Type-Options: nosniff");
header("X-Frame-Options: DENY");
header("X-XSS-Protection: 1; mode=block");

Priority 4: Add Query Logging (LOW)

Log suspicious search patterns:

// Detect potential attacks
if (preg_match('/[%_]{10,}/', $params['query'])) {
    error_log("Suspicious search pattern from {$_SERVER['REMOTE_ADDR']}: {$params['query']}");
}

Security Best Practices Checklist

  • Use prepared statements (SQL injection)
  • Escape output with htmlspecialchars() (XSS)
  • Escape LIKE wildcards (wildcard injection)
  • Validate input lengths (DoS)
  • Implement rate limiting (DoS)
  • Validate pagination limits (resource waste)
  • Restrict to published data only (access control)
  • Add security headers (defense in depth)
  • Log suspicious activity (monitoring)
  • Use HTTPS in production (encryption)

Testing Security

Test 1: SQL Injection

# These should NOT cause errors or expose data
curl "search.php?query=' OR 1=1--"
curl "search.php?query='; DROP TABLE theses;--"
curl "search.php?year=' OR '1'='1"

Expected: Treated as literal search strings, no SQL execution

Test 2: XSS

curl "search.php?query=<script>alert('XSS')</script>"

Expected: Script tags displayed as text, not executed

Test 3: Wildcard Injection

curl "search.php?query=%"

Current: Returns all results After fix: Searches for literal "%" character

Test 4: DoS via Long Input

curl "search.php?query=$(python3 -c 'print("a"*100000)')"

Current: Processes full string After fix: Rejects with error


Conclusion

Current Status: The search system has good baseline security against SQL injection and XSS, but needs hardening for production use.

Recommended Actions:

  1. Apply wildcard escaping (use Database_secure.php)
  2. Add input length validation
  3. Implement rate limiting
  4. Add security headers
  5. Monitor for suspicious patterns

Risk Level:

  • Current: Medium (suitable for internal/development use)
  • After improvements: Low (production-ready)