PHP Security: XSS and SQL Injection
Three of the most common ways PHP apps get compromised are Cross-Site Scripting (XSS), SQL Injection, and storing passwords in plain text. This page explains each one and shows the real PHP functions that prevent them, with genuinely-executed live demos.
1) Cross-Site Scripting (XSS)
XSS happens when user-supplied text is echoed into HTML without escaping, so a malicious visitor
can submit something like <script>alert(1)</script> and have it execute in
other users' browsers (in a comment box, profile name, search results page, etc.).
<?php
// DANGEROUS: never do this with user input
echo "Hello, " . $_GET['name'];
?>
<?php
echo "Hello, " . htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');
?>
ENT_QUOTES escapes both single and double quotes too, so the output is safe inside
value="..." attributes as well as regular text.
2) Live Demo: Escaped vs Unsafe Output
htmlspecialchars()
so anything you type is shown as harmless text either way. The first box is labeled to show you exactly
what an unescaped version would have let happen; the second box is the real, correctly-escaped
output you should always use in production.
3) SQL Injection
SQL Injection happens when user input is concatenated directly into a SQL query string, letting an attacker change the query's meaning.
<?php
// DANGEROUS: string concatenation
$sql = "SELECT * FROM users WHERE email = '" . $_POST['email'] . "'";
?>
<?php
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$_POST['email']]);
$user = $stmt->fetch();
?>
php-mysql.php for a fuller walkthrough of connecting with PDO and
running prepared queries.
4) Password Hashing
Never store plain-text passwords — if your database ever leaks, every user's real password leaks
with it. Instead, hash the password with password_hash() and only ever compare hashes
with password_verify().
<?php
// When the user registers:
$hash = password_hash($plainPassword, PASSWORD_DEFAULT);
// store $hash in the database, never $plainPassword
// When the user logs in:
if (password_verify($plainPassword, $hash)) {
echo 'Login OK';
}
?>
5) Live Demo: password_hash() and password_verify()
6) A Word on CSRF
Cross-Site Request Forgery (CSRF) tricks a logged-in user's browser into submitting a request they
didn't intend. The Forms & User Input page already implements a real
CSRF token pattern (session token + hidden input + hash_equals()) — the same pattern
protects both demo forms on this page.
htmlspecialchars(), never build SQL with string
concatenation, always hash passwords with password_hash(), and always include a CSRF token
on state-changing forms.