Лучшие практики и рекомендации по защите php-приложений от XSS-атак
Создание функционирующего веб-приложения – это только полдела. Современные онлайн-сервисы и веб-приложения, помимо собственного контента, хранят данные пользователей. Защита этих данных зависит от правильно написанного кода с точки зрения надёжности и безопасности.
Большинство уязвимостей связано с неправильной обработкой данных, получаемых извне, или недостаточно строгой их проверкой. Одной из таких уязвимостей является межсайтовое выполнение сценариев (Сross Site Sсriрting, XSS), которая может привести к дефейсу сайта, перенаправлению пользователя на зараженный ресурс, вставке в веб-ресурс вредоносного кода, краже COOKIE-файлов, сессии и прочей информации. Противостоять XSS своими сила поможет применение лучших практик и рекомендаций по безопасному программированию, о которых и пойдет речь ниже.
Лучшие практики и рекомендации:
1. Используйте экранирование выходных данных. Применяйте встроенные функции для очистки кода от вредоносных скриптов. К ним относятся такие функции как htmlspecialchar(), htmlentities() и strip_tags().
Примеры использования:
$name = strip_tags($_POST['name']);
$name = htmlentities($_POST['name'], ENT_QUOTES, "UTF-8");
$name = htmlspecialchars($_POST['name'], ENT_QUOTES);
Встроенные функции PHP, в отличие от самописных, работают гораздо быстрее, а также имеют меньше ошибок безопасности и уязвимостей, т.к. постоянно совершенствуются. Также рекомендуется использовать специальные библиотеки, построенные на основе встроенных функций и фильтров. В качестве примера можно привести OWASP Enterprise Security API (ESAPI), HTML Purifier, Reform, ModSecurity.
Для того чтобы библиотека работала правильно, её нужно предварительно настроить!
2. Используйте подход «белые списки». Подход работает по принципу «что не разрешено, то запрещено». Это стандартный механизм валидации полей для проверки всех входных данных, включая заголовки, куки, строки запросов, скрытые поля, а также длина полей форм, их тип, синтаксис, допустимые символы и другие правила, прежде чем принять данные, которые будут сохраненные и отображены на сайте. Например, если в поле нужно указать фамилию, необходимо разрешить только буквы, дефис и пробелы. Если отклонить все остальное, то фамилия д’Арк будет отклонена — лучше отклонить достоверную информацию, чем принять вредоносные данные.
К сожалению, со своей задачей встроенные фильтры валидации данных PHP не справляются, поэтому рекомендуется писать собственные фильтры и «допиливать» их по мере необходимости. Таким образом, со временем ваши входные методы фильтрации будут усовершенствованы. Стоит также не забывать, что существует слишком много типов активного содержимого и способов кодирования для обхода подобных фильтров. По этой же причине не используйте проверку по «черному списку».
3. Указывайте кодировку на каждой веб-странице. Для каждой веб-страницы необходимо указывать кодировку (например, ISO-8859-1 или UTF-8) до каких-либо пользовательских полей.
Пример использования:
<?php
header("Content-Type: text/html; charset=utf-8");
?>
<!DOCTYPE html>
<html>
<head>
<title>Сharset</title>
<meta charset="utf-8">
</head>
или в файле .htaccess веб-сервера Apache дописать строчку:
AddDefaultCharset UTF-8
Если в http-заголовке или в метатегах кодировка не указана, браузер пытается сам определить кодировку страницы. Стандарт HTML 5 не рекомендует использовать такие кодировки, которые включают JIS_C6226-1983, JIS_X0212-1990, HZ-GB-2312, JOHAB (Windows code page 1361), а также кодировки, основанные на ISO-2022 и EBCDIC. Кроме того, веб-разработчики не должны использовать CESU-8, UTF-7, BOCU-1 и кодировки SCSU. Эти кодировки никогда не предназначались для веб-контента[1]. В случае если тег расположен до тега и заполняется пользовательскими данными, злоумышленник может вставить вредоносный html-код в кодировке UTF-7, обойдя, таким образом, фильтрацию таких символов, как ‘<’ и ‘"’.
4. Установить флаг HttpOnly. Этот Флаг делает клиентские куки недоступными через языки сценариев, такие как JavaScript.
Данная настройка активируется
— в php.ini [2]:
session.cookie_httponly = True
— в скрипте через функцию session_set_cookie_params() [3]:
void session_set_cookie_params ( int $lifetime [, string $path [, string $domain [, bool $secure = false [, bool $httponly = true ]]]] )
— в веб-приложении через функцию setcookie() [4]:
bool setcookie ( string $name [, string $value [, int $expire = 0 [, string $path [, string $domain [, bool $secure = false [, bool $httponly = true ]]]]]] )
Эта функция поддерживается последними версиями распространенных браузеров. Однако старые версии некоторых браузеров через XMLHttpRequest и другие мощные браузерные технологии обеспечивают доступ для чтения HTTP-заголовков, в том числе и заголовка Set-Cookie, в котором установлен флаг HttpOnly[5].
5. Использовать Content Security Policy (CSP). Это заголовок, который позволяет в явном виде объявить «белый список» источников, с которых можно подгружать различные данные, например, JS, CSS, изображения и пр. Даже если злоумышленнику удастся внедрить скрипт в веб-страницу, он не выполниться, если не будет соответствовать разрешенному списку источников.
Для того чтобы воспользоваться CSP, веб-приложение должно через HTTP-заголовок «Content-Security-Policy» посылать политику браузеру.
Пример использования:
Content-Security-Policy: default-src 'self';
script-src trustedscripts.example.com
style-src 'self' ajax.googleapis.com;
connect-src 'self' https://api.myapp.com realtime.myapp.com:8080;
media-src 'self' youtube.com;
object-src media1.example.com media2.example.com *.cdn.example.com;
frame-src 'self' youtube.com embed.ly
'Content-Security-Policy' — это официальный http-заголовок, утвержденный W3C, который поддерживается браузерами Chrome 26+, Firefox 24+ и Safari 7+. HTTP-заголовок «X-Content-Security-Policy» используется для Firefox 4-23 и для IE 10-11, заголовок «X-Webkit-CSP» – для Chrome 14-25, Safari 5.1-7[6].
С позиции веб-разработчика правильно и грамотно развернуть CSP на своем ресурсе довольно проблематично, так как отдельную политику необходимо устанавливать для каждой страницы сайта.
6. Регулярно проводите анализ безопасности кода и тестирование на проникновение. Используйте как ручной, так и автоматизированный подходы. Такие инструменты как Nessus, Nikto и OWASP Zed Attack Proxy помогут обнаружить уязвимости XSS в вашем веб-приложении.
7. Пользователям рекомендуется регулярно обновлять браузер до новой версии и использовать для них расширения, например, NoScript.
Как вы могли заметить, у каждой рекомендации есть свои достоинства и недостатки, поэтому эффективность противодействия межсайтовому выполнению сценария достигается путем применения комплексной защиты, т.е. использования описанных рекомендаций в совокупности.
Полезные ссылки:
1. HTML Standart. The elements of HTML.
2. PHP: Настройка во время выполнения.
3. PHP: session_set_cookie_params.
4. PHP: setcookie.
5. OWASP HttpOnly.
6. Can I use Content Security Policy.
7. PHP Security Guide.
8. 2011 CWE/SANS Top 25 Most Dangerous Software Errors. CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').
9. Introducing Content Security Policy.
10. OWASP XSS.
11. OWASP Top 10 2013-A3-Cross-Site Scripting (XSS).
12. OWASP XSS Filter Evasion Cheat Sheet.
Автор статьи: Внештатный сотрудник PentestIT, Сергей Сторчак
Автор: pentestit-team