Security Basics: SQL Injection and XSS
Preventing SQL injection with prepared statements, and XSS with output escaping.
What you'll learn
- Explain why string-concatenated SQL queries are vulnerable to SQL injection
- Describe how prepared statements with bound parameters prevent that vulnerability
- Predict the output of escaping untrusted content with `htmlspecialchars()` before displaying it
Explanation
SQL injection happens when untrusted input is concatenated directly into a SQL query string: "SELECT * FROM users WHERE name = '" . $_GET["name"] . "'". A visitor supplying a crafted name value can inject their own SQL, changing the query's meaning entirely -- a well-known, serious vulnerability class.
The fix is prepared statements with bound parameters, supported by PHP's PDO extension: you write the query with placeholders ("SELECT * FROM users WHERE name = ?", or a named placeholder like :name), then bind the actual value separately. The database driver keeps the query structure and the data strictly separate, so a malicious value can never be interpreted as part of the SQL itself -- it's treated purely as data, no matter what it contains.
Cross-site scripting (XSS) is the same category of problem in a different place: if you echo untrusted input directly into an HTML page, a visitor who submits <script>...`` as their input gets that script tag rendered -- and executed -- in every other visitor's browser who views that page. The fix is **output escaping**: htmlspecialchars($value, ENT_QUOTES) converts HTML-special characters (<, >, ", ', &`) into their safe HTML-entity equivalents before they're ever placed into an HTML response, so the browser displays them as literal text instead of interpreting them as markup.
Both fixes share the same underlying principle: never let untrusted input be interpreted as code (SQL syntax, HTML markup) -- always treat it strictly as data, using the tool built for that boundary (parameter binding for SQL, escaping for HTML) rather than trying to manually filter dangerous-looking substrings yourself.
Guided lab
Predict: Escaping untrusted output
Read this script and predict exactly what it sends to the browser.
<?php
$userInput = '<script>alert("hi")</script>';
$unsafeOutput = "Comment: " . $userInput;
$safeOutput = "Comment: " . htmlspecialchars($userInput, ENT_QUOTES);
echo $unsafeOutput . "\n";
echo $safeOutput . "\n";Stuck? Get a hint.
Common mistakes
- Building a SQL query by directly concatenating `$_GET`/`$_POST` values into the query string instead of using prepared statements with bound parameters.
- Trying to prevent SQL injection by manually stripping or escaping specific characters yourself instead of using parameter binding, which the database driver guarantees handles it correctly.
- Echoing untrusted input directly into HTML output without `htmlspecialchars()`, opening the page to cross-site scripting.
Knowledge check
Takeaway
Never let untrusted input be interpreted as code -- use prepared statements with bound parameters for SQL, and `htmlspecialchars()` on any untrusted value before it reaches HTML output.
Summary
SQL injection and XSS are both about untrusted data being misinterpreted as code; prepared statements (SQL) and htmlspecialchars() (HTML output) enforce the data/code boundary correctly.
References
Your notes
Notes save automatically.
Finished this lesson?
Mark it complete to track your progress and schedule a future review.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.