Question
How to Sanitize User Input in PHP for SQL Injection and XSS
Question
I want to sanitize user input in PHP in a way that protects against both SQL injection and XSS attacks. Is there a single catch-all function that can handle both problems effectively while still allowing certain HTML tags in the submitted content?
Short Answer
By the end of this page, you will understand why there is no single PHP function that safely handles all kinds of user input. You will learn the difference between validation, sanitization, SQL injection prevention, and XSS prevention, and how to correctly combine tools such as prepared statements, output escaping, and HTML allow-list filtering.
Concept
There is no single catch-all function in PHP that can safely solve SQL injection, XSS, and allowed HTML filtering at the same time.
These are different problems, and each requires a different solution.
The three separate concerns
1. SQL injection prevention
SQL injection happens when user input is mixed directly into an SQL query string.
The correct solution is:
- Use prepared statements
- Bind parameters
- Do not manually escape SQL unless absolutely necessary
In modern PHP, this usually means using PDO or MySQLi prepared statements.
2. XSS prevention
XSS happens when unsafe content is displayed in HTML and the browser interprets it as code.
The correct solution is:
- Escape output when rendering HTML
- In PHP, this usually means
htmlspecialchars()
Important: XSS prevention is generally an output problem, not an input problem.
3. Allowing some HTML tags
Sometimes you want to allow formatting tags such as <b>, <i>, or <p>.
This is more complex than simply stripping tags. If you allow HTML, you must make sure:
- only approved tags are allowed
- dangerous attributes like
onclickare removed - dangerous URLs such as
javascript:are blocked
Mental Model
Think of user input like a package arriving at a building.
- Validation checks whether the package is the right type: is this really an email, age, or comment?
- Prepared statements are like a secure loading dock for the database: the package contents cannot secretly rewrite the delivery instructions.
- Output escaping is like wrapping dangerous items before bringing them into a room: the browser sees text, not executable code.
- HTML sanitizing is like a security checkpoint that allows only approved items through, such as
<b>and<p>, while removing dangerous ones.
The important lesson is that one security guard cannot do every job. Each step protects a different part of the system.
Syntax and Examples
1. Prevent SQL injection with prepared statements
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$name = $_POST['name'] ?? '';
$stmt = $pdo->prepare('SELECT * FROM users WHERE name = :name');
$stmt->execute(['name' => $name]);
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
Here, :name is a placeholder. The input is sent separately from the SQL command, so it cannot change the query structure.
2. Prevent XSS with output escaping
<?php
$comment = $_POST['comment'] ?? '';
echo htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');
If the user submits:
Step by Step Execution
Consider this example:
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$comment = $_POST['comment'] ?? '';
$stmt = $pdo->prepare('INSERT INTO comments (body) VALUES (:body)');
$stmt->execute(['body' => $comment]);
echo htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');
What happens step by step
1. Read input
$comment = $_POST['comment'] ?? '';
- If the form sent
comment, its value is used. - Otherwise, an empty string is used.
Example input:
Hello <>
Real World Use Cases
Comment systems
Users submit comments that may contain text, punctuation, quotes, and sometimes limited formatting. A typical approach is:
- validate comment length
- store using prepared statements
- escape output for plain text comments
- or sanitize with an HTML allow-list for rich text comments
Blog or CMS editors
An admin or trusted user may be allowed to use tags like <p>, <h2>, and <a>. In this case:
- use a dedicated HTML sanitizer
- restrict tags and attributes
- reject dangerous protocols and event handlers
Search forms
A search term should not be inserted directly into SQL. Instead:
- pass the search text as a bound parameter
- escape the search text when redisplaying it in the page
Profile fields
Fields such as username, bio, and location all need different handling:
- username: validate allowed characters
- bio: maybe allow limited formatting
- location: often plain text only
APIs and admin panels
Even internal tools need safe handling. Admin users can accidentally paste unsafe HTML or malformed data. Security rules should still apply.
Real Codebase Usage
In real PHP projects, developers usually do not create one giant sanitize() function for everything. Instead, they use a few clear patterns.
1. Validate early
Developers check input as soon as it enters the application.
<?php
$title = trim($_POST['title'] ?? '');
if ($title === '') {
throw new InvalidArgumentException('Title is required.');
}
This is often called validation at the boundary.
2. Use guard clauses
Guard clauses stop bad input early.
<?php
$age = $_POST['age'] ?? null;
if (!filter_var($age, FILTER_VALIDATE_INT)) {
die('Age must be a valid integer.');
}
3. Store data safely, then escape on output
A common pattern is:
- validate input shape
- store with prepared statements
Common Mistakes
Mistake 1: Using one function for every security problem
Broken idea:
<?php
$input = addslashes($_POST['input']);
Why it is wrong:
addslashes()is not a complete SQL injection defense- it does not solve XSS
- it depends on database context and is easy to misuse
Use instead:
- prepared statements for SQL
htmlspecialchars()for HTML output
Mistake 2: Escaping input before storing it
Broken code:
<?php
$comment = htmlspecialchars($_POST['comment'], ENT_QUOTES, 'UTF-8');
// store $comment in database
Why it is a problem:
- you mix storage with presentation
- the data is now HTML-specific
- output in another context becomes awkward or wrong
Better approach:
- store raw or validated/sanitized data
- escape when rendering
Mistake 3: Relying on strip_tags() for safe HTML
Comparisons
| Problem | Wrong or weak approach | Better approach | Why |
|---|---|---|---|
| SQL injection | addslashes() or string concatenation | Prepared statements | Separates SQL code from user data |
| XSS in HTML output | Trying to clean all input globally | htmlspecialchars() on output | Escapes data for the browser context |
| Allowing some HTML | strip_tags() only | HTML Purifier or similar allow-list sanitizer | Removes dangerous tags and attributes safely |
| Checking input format | Blindly accepting everything | Validation with filter_var(), regex, or custom rules | Ensures input matches expected shape |
htmlspecialchars() vs
Cheat Sheet
Core rules
- There is no single catch-all sanitize function for all security problems.
- Use prepared statements to prevent SQL injection.
- Use
htmlspecialchars()to prevent XSS in HTML output. - Use validation to make sure input matches expected formats.
- If you want to allow some HTML, use an allow-list HTML sanitizer such as HTML Purifier.
Safe patterns
SQL query
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute(['email' => $email]);
HTML output
echo htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
Email validation
filter_var($email, FILTER_VALIDATE_EMAIL)
Plain text only
$plain = ();
FAQ
Is there a single PHP function to prevent both SQL injection and XSS?
No. SQL injection and XSS are different problems. Use prepared statements for SQL and output escaping for HTML.
Should I sanitize input before saving it to the database?
Usually, validate it before saving, but do not HTML-escape everything before storage. Escape when outputting the data.
Is htmlspecialchars() enough to stop XSS?
It is the standard solution for plain text in HTML output. If you want to allow HTML, use a proper HTML sanitizer instead.
Is strip_tags() safe for allowing a few tags?
Not by itself. It can remove tags, but it does not fully protect against dangerous attributes or complex HTML payloads.
What should I use in PHP for SQL injection prevention?
Use PDO or MySQLi prepared statements with bound parameters.
When should I use filter_var()?
Use it for validation, such as checking emails, integers, and URLs. It does not replace SQL protection or XSS protection.
Can I trust admin or logged-in users more?
You can trust them more for permissions, but their input should still be handled safely. Mistakes and unsafe pasted content can still happen.
Mini Project
Description
Build a simple PHP comment form that accepts a user's name and comment, stores the data safely, and displays it without allowing XSS. The project demonstrates the correct separation of concerns: validation for expected input, prepared statements for database safety, and output escaping for HTML rendering.
Goal
Create a secure comment submission flow in PHP that prevents SQL injection and safely displays user content.
Requirements
- Accept a name and a comment from an HTML form.
- Reject empty values after trimming whitespace.
- Insert the comment into a database using a prepared statement.
- Display saved comments using safe HTML escaping.
- Keep comments as plain text only for this version.
Keep learning
Related questions
Converting HTML and CSS to PDF in PHP: Core Concepts, Limits, and Practical Approaches
Learn how HTML-to-PDF conversion works in PHP, why CSS support varies, and how to choose practical approaches for reliable PDF output.
How PHP foreach Actually Works with Arrays
Learn how PHP foreach works internally, including array copies, internal pointers, by-value vs by-reference behavior, and common pitfalls.
How to Check String Prefixes and Suffixes in PHP
Learn how to check whether a string starts or ends with specific text in PHP using simple functions and practical examples.