DEV3 Technologies home

DEV3 Developer Forum

How do I prevent XSS when printing user input in PHP?

PHP helpSolved0 views
dev3 ·

I echo $_GET['q'] and comments from my database straight into HTML. Someone injected JavaScript in a comment. What is the correct escaping in PHP, and does it differ for attributes, JavaScript and URLs?

#php#security#xss

0

1 reply

dev3 · Accepted answer

XSS happens when untrusted data is placed into a page without being encoded for the context it lands in. The rule is: escape on output, for the exact context.

HTML body and quoted attributes

function e(?string $s): string
{
    return htmlspecialchars($s ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
<p>Search results for <?= e($_GET['q'] ?? '') ?></p>
<input type="text" name="q" value="<?= e($q) ?>">

Always pass ENT_QUOTES (so single quotes are encoded) and the charset. Since PHP 8.1 the default flags include ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, but being explicit keeps older servers safe. Always quote attribute values.

URLs

<a href="/search?q=<?= urlencode($q) ?>">...</a>

and validate scheme for user-provided links: allow only http/https, reject script-style schemes such as the javascript pseudo-protocol.

$scheme = parse_url($url, PHP_URL_SCHEME);
if (!in_array($scheme, ['http', 'https'], true)) { $url = '#'; }

JavaScript context

Never concatenate into script text. Pass data as JSON:

$json = json_encode($user, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_THROW_ON_ERROR);
// print it inside an inline script as: const user = <?= $json ?>;

Allowing some HTML (comments, rich text)

htmlspecialchars removes all formatting. For rich text use a sanitizer library like HTMLPurifier with an allow list, not strip_tags() or regex.

Defence in depth

  • Content-Security-Policy header limiting scripts (a nonce-based policy; see the CSP headers generator).
  • Session cookie with HttpOnly, so scripts cannot read it.
  • Template engines such as Twig and Blade escape by default; raw output (|raw, {!! !!}) is the dangerous path.

Pitfalls

  • Do not escape on input and store the escaped text: you will double-escape and the data is wrong in other contexts (JSON, email, PDF).
  • addslashes, mysqli_real_escape_string and strip_tags do not prevent XSS.
  • htmlentities is for rare cases; htmlspecialchars is the right default.
0

Your reply

Every post is reviewed by DEV3 before it appears. Usually reviewed within 24 hours.

Markdown supported. Use ``` for code blocks. Images are not allowed.