flawopen.com/Cross-Site Scripting/PHP
Imagine a guestbook where visitors write a public note. Stored XSS is like someone writing a note that isn't just text — it's a hidden trick that makes the guestbook page itself start doing things, like stealing the next visitor's login session, the moment anyone opens the page to read it.
Cross-site scripting happens when untrusted input reaches the rendered page without proper output encoding. PHP has no auto-escaping at all for a plain echo — unlike a templating engine, safety is entirely manual unless you use one that escapes by default (like Twig).
In 2005, an 19-year-old user exploited a stored XSS flaw on MySpace to create the "Samy" worm — a script that added itself to every profile that viewed it, infecting over a million profiles within about 20 hours and forcing MySpace offline to contain it. It remains one of the most-cited demonstrations of how quickly stored XSS can self-propagate.
Source: widely documented in security industry retrospectives — see References below.// $_GET value echoed with no encoding
echo "<div>Welcome, "
. $_GET['name'] . "</div>";
// value HTML-encoded before output
echo "<div>Welcome, " . htmlspecialchars(
$_GET['name'], ENT_QUOTES, 'UTF-8'
) . "</div>";
htmlspecialchars() converts <, >, &, and quote characters into their HTML entity equivalents before the string reaches the response — the browser displays them as visible text instead of parsing them as markup. Plain echo performs no such conversion; it writes exactly what it's given.
Without ENT_QUOTES, htmlspecialchars() only encodes double quotes by default in older PHP versions, leaving single-quote-delimited HTML attributes still breakable. Always pass it explicitly.
Unlike Django, Rails, or Flask/Jinja2, plain PHP templates (raw .php files mixing HTML and <?= ?>) provide zero default protection — every single output point needs its own htmlspecialchars() call, or a templating layer like Twig (which escapes by default) needs to be adopted.
htmlspecialchars() is correct for HTML body and attribute context. A value reflected into an inline <script> block or a href needs different, context-specific encoding — one function doesn't cover every output context.
Input sanitization and output encoding are different defenses for different purposes — sanitizing on the way in doesn't guarantee every output context downstream is safe, especially if the same field is rendered in HTML, an attribute, and a JS context in different places.
True for standard {{ }} output — false for the explicit |raw filter, Twig's own named escape hatch.
grep -rn "echo.*\$_\(GET\|POST\|REQUEST\)" --include="*.php" . | grep -v htmlspecialchars
grep -rn "|raw" --include="*.twig" .
Same underlying shape — untrusted data mixed into a command's structure without encoding — but the target and damage differ: SQL injection targets the database, XSS targets other users' browsers.
Yes for its standard {{ }} syntax, which escapes by default. Blade's {!! !!} is its explicit unescaped-output syntax — the same risk as |raw in Twig if used on untrusted data.