Output encoding — это тема, которой многие разработчики не уделяют достаточно внимания, но крайне важная для безопасности. Основная идея проста: когда вы выводите данные, полученные от пользователя, нужно правильно их форматировать для данного контекста. В HTML — HTML encoding, в JavaScript — JavaScript encoding, в URL — URL encoding. Незнание этих различий приводит к самым большим XSS уязвимостям.
Encoding в HTML контексте
Самый частый случай — вывод введенного пользователем текста на HTML страницу. В PHP для этого используется функция htmlspecialchars(). Но ее нужно использовать правильно: htmlspecialchars($текст, ENT_QUOTES | ENT_HTML5, 'UTF-8'). Флаг ENT_QUOTES кодирует и одинарные, и двойные кавычки. ENT_HTML5 соответствует современному стандарту HTML. 'UTF-8' для безопасности кодировки. Если вы напишете только htmlspecialchars($текст), одинарные кавычки не будут закодированы и будет уязвимость в контексте атрибута.
Encoding в JavaScript контексте
Иногда нужно передать значение из PHP в JavaScript. Например, <script>var имя = "$имя";</script>. Здесь htmlspecialchars() недостаточно — потому что в контексте JavaScript символы \\ и \\n имеют другое значение. Правильный способ — использовать json_encode(): <script>var имя = <?= json_encode($имя, JSON_HEX_TAG | JSON_HEX_AMP) ?>;</script>. json_encode автоматически переводит все опасные символы в безопасный формат и возвращает строку, обернутую двойными кавычками.
Encoding в URL контексте
При добавлении параметра в URL нужно использовать urlencode() или rawurlencode(). Например, ссылка на страницу результатов поиска: <a href="search.php?q=<?= urlencode($q) ?>">. Эта функция переводит специальные символы как & = ? # в безопасный формат. Если не использовать urlencode, атакующий может добавить "&admin=1" к параметру q и получить права администратора.
Content Security Policy — дополнительный слой
CSP — это способ сказать браузеру "на этой странице можно загружать данные только из этих источников". Через заголовок CSP вы можете указать, что JavaScript только с вашего домена, изображения только с определенного CDN, style только inline разрешен. Даже если есть XSS уязвимость, CSP препятствует загрузке внешних скриптов атакующим.
DOM-based XSS — отдельный тип
Иногда XSS происходит не в серверном коде, а в JavaScript коде. Например, document.write(location.hash) или innerHTML = userInput. Это называется DOM-based XSS и не видно со стороны сервера. Защита — всегда использовать безопасные функции в JavaScript: textContent (вместо innerHTML), createElement (вместо document.write).
Практика Sayt.uz
На платформе Sayt.uz все введенные пользователем данные перед выводом фильтруются htmlspecialchars(). В JSON ответах используется json_encode. В URL параметрах применяется urlencode. Заголовки Content Security Policy активированы. Разместив свой сайт на хостинге Sayt.uz, можно использовать дополнительные защитные слои на уровне сервера.