PHP security explainer · defensive
XSS in PHP: what it is and how to prevent it
Cross-site scripting is still one of the most common web bugs. In PHP it usually comes down to printing data into HTML without escaping it for the place it lands.
Last updated
What it is
Reflected XSS echoes a value from the current request; stored XSS saves it, for example in a comment, and serves it to everyone; DOM-based XSS happens in client-side JavaScript. In each case the browser runs script with your site’s privileges, which can steal sessions or act as the user. MITRE classifies it as CWE-79.
How to detect it
- Search templates and PHP files for
echo,printand<?=that output variables withouthtmlspecialchars()or a template escape filter. - Pay special attention to values placed in HTML attributes, inline JavaScript, URLs and CSS, where HTML escaping alone is not enough.
- Use your framework’s security linting and a scanner such as OWASP ZAP against a staging copy you own.
How to fix it
- Escape on output:
htmlspecialchars($v, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, 'UTF-8')for HTML body and quoted attributes. - Use a template engine with auto-escaping on (Twig, Blade, Latte) and avoid raw-output filters for untrusted data.
- Use
json_encode()withJSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOTwhen putting data into script blocks, and validate URLs before using them in href. - Add a Content-Security-Policy and set session cookies HttpOnly, Secure and SameSite as defense in depth.
Read-only checks and safe patterns
# Find raw echo of request data (read-only, rough first pass)
grep -rnE "echo\s+\$_(GET|POST|REQUEST|COOKIE)" --include=*.php . | grep -v /vendor/
// Escape on output, for the HTML context
echo htmlspecialchars($name, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, 'UTF-8');
# Defense in depth: a Content-Security-Policy header (example)
Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'Should I strip tags from input instead?
Filtering input helps, but escaping on output for the right context is what reliably prevents XSS. OWASP recommends output encoding first.
Does strip_tags() prevent XSS?
Not reliably. It does not handle attribute or JavaScript contexts and is easy to misuse. Use context-aware escaping.
Other PHP vulnerability classes
- PHP object injection: what it is and how to fix itPHP object injection happens when user-controlled input reaches unserialize(), which can instantiate objects whose magic methods run attacker-chosen logic.
- PHP file inclusion (LFI/RFI): what it is and how to fix itFile inclusion happens when user input controls the path passed to include or require, letting an attacker read local files (LFI) or, with allow_url_include on, run remote code (RFI).
- SSRF in PHP: what it is and how to fix itServer-side request forgery (SSRF) happens when a PHP app fetches a URL the user controls, letting an attacker reach internal services or cloud metadata endpoints through your server.
- phpinfo() exposure: why it matters and how to fix itA public phpinfo() page reveals the exact PHP version, loaded modules, file paths and often environment variables or secrets, which helps attackers target bugs such as PHP2Shell.
Sources
Outbound links leave this site. We do not host exploit code.
- CWE-79: Cross-site Scripting — MITRE CWE
- Cross Site Scripting Prevention Cheat Sheet — OWASP Cheat Sheet Series
- htmlspecialchars() — PHP Manual