Вторая линия обороны от XSS - CSP (Content Security Policy)
Первая линия обороны от XSS - это обработка и санитизация пользовательского ввода для обеспечение безопасности содержимого. Однако это проще сказать, чем сделать, особенно для устаревших проектов с запутанным и сложным кодом. Становится сложно определить, где следует внести необходимые исправления.
Кроме того, ошибки могут произойти при написании кода, и есть три основные причины возникновения проблем безопасности:
1. Вы не знаете, что определенные действия могут привести к проблемам. 2. Вы забываете, что определенные действия могут привести к проблемам. 3. Вы знаете, что определенные действия могут привести к проблемам, но из-за сроков выполнения проекта или указаний руководства, вы предпочитаете их игнорировать.
Первая причина аналогична упомянутому ранее примеру с тегом <a href>, в котором вы, возможно, не знаете, что он может исполнять код с помощью javascript:.
Вторая причина - когда вы знаете о уязвимостях XSS и знаете, что вывод должен быть закодирован, но забываете это сделать.
Третья причина - это когда вы осознанно оставляете уязвимость без исправления, хотя она должна быть исправлена, из-за ограничений по времени проекта или указаний руководства.
В случае с первой причиной, когда у вас нет представления о том, где следует решить проблему, или о наличии уязвимости, как можно защититься от нее? Вот здесь нам и понадобится CSP.
Автоматический механизм защиты: Content Security Policy
CSP, сокращенно от Content Security Policy, позволяет вам устанавливать правила для вашей веб-страницы и информировать браузер, что разрешено только содержимое, которое соответствует этим правилам. Любой контент, который не соответствует, должен быть заблокирован.
Есть два способа добавления CSP на веб-страницу. Один через заголовок HTTP-ответа Content-Security-Policy, и другой через тег <meta>. Поскольку последний способ проще для демонстрации, мы сосредоточимся на нем (хотя более часто используется первый, так как некоторые правила можно установить только через него).
(Существует еще один загадочный третий метод, связанный с атрибутом csp тега <iframe>, но мы не будем здесь обсуждать это.)
Посмотрим на пример:
На этой веб-странице CSP объявлена как script-src 'none', что означает "на этой веб-странице не разрешено никакое исполнение скрипта". Соответственно, скрипт в теле не будет выполнен. Если вы откроете инструменты разработчика, вы увидите следующее сообщение об ошибке:
> Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'none'". Either the 'unsafe-inline' keyword, a hash ('sha256-bhHHL3z2vDgxUt0W3dWQOrprscmda2Y5pLsLg4GF+pI='), or a nonce ('nonce-...') is required to enable inline execution.
Вот почему я называю CSP второй линией обороны. Когда первая линия обороны (обработка пользовательского ввода) не работает, вы все еще можете полагаться на CSP, чтобы предотвратить загрузку скриптов или других ресурсов, эффективно предотвращая уязвимости XSS.
Правила CSP
CSP позволяет вам определить директивы вместе с правилами. В приведенном выше примере директива script-src установлена в 'none', что в конечном итоге блокирует выполнение любого JavaScript.
Во-первых, важно отметить, что директиву script-src не следует легко интерпретировать как "атрибут src тегов script". Здесь слово "script" относится к общему понятию "скриптов", а не специально к тегам скриптов или атрибутам src.
Например, если на странице есть фрагмент HTML <a href="javascript:alert(1)">click</a>, который не имеет тега скрипта или атрибута src, он все равно будет заблокирован CSP, и будет отображено сообщение об ошибке. Это происходит потому, что script-src 'none' означает "block the execution of any JavaScript", будь это через теги скриптов, обработчики событий или псевдо-протокол javascript:.
Итак, какие доступны директивы?
Другие часто используемые директивы включают: 1. script-src: Управляет JavaScript 2. style-src: Управляет CSS 3. font-src: Управляет шрифтами 4. img-src: Управляет изображениями 5. connect-src: Управляет подключениями (fetch, XMLHttpRequest, WebSocket и т.д.) 6. media-src: Управляет видео и аудио 7. frame-src: Управляет фреймами и iframes 8. base-uri: Управляет использованием `<base>` 9. form-action: Управляет действиями форм 10. frame-ancestors: Управляет тем, какие страницы могут внедрять текущую страницу 11. report-uri: Будет обсуждено позже 12. navigate-to: Управляет тем, куда может направляться страница
Существует много видов, верно? И этот список может изменяться. Например, navigate-to в конце - это более новая функция, которая еще не поддерживается современными браузерами.
Так что какие возможны правила для каждого из них? В зависимости от директив могут использоваться разные правила.
Наиболее часто используемые правила таковы:
Например, script-src * по сути эквивалентно отсутствию какого-либо правила (разрешает все URL, но стоит отметить, что встроенный скрипт все равно блокируется), в то время как script-src 'none' полностью блокирует выполнение любого JavaScript.
Более того, некоторые правила можно объединять. На практике вы часто видите правила вроде этого:
Полная CSP - это комбинация этих правил, с директивами, разделенными ;, вот так:
С помощью CSP мы можем сообщить браузеру, какие ресурсы разрешено загружать, а какие нет. Даже если атакующий найдет точку инъекции, он может не суметь выполнить JavaScript, таким образом, снижая воздействие уязвимости XSS (хотя ее все равно нужно исправить, но риск меньше).
Правила для script-src
Помимо указания URL ресурсов для загрузки, существуют другие правила, которые можно использовать.
Например, после установки CSP, встроенные скрипты и eval блокируются по умолчанию. Заблокированы следующие встроенные скрипты:
1. Код, прямо размещенный внутри тегов <script> (должен быть загружен из внешнего источника с использованием <script src>) 2. Обработчики событий, написанные в HTML, такие как onclick 3. Псевдопротокол javascript:
Чтобы разрешить использование встроенных скриптов, необходимо добавить правило 'unsafe-inline'.
И если вы хотите выполнить код, как если бы он был eval, вам нужно добавить правило 'unsafe-eval'. Некоторые люди могут знать, что setTimeout также может выполнять код в виде строки, вот так:
Аналогично, setInterval, Function и другие могут добиться того же, но все они требуют использования правила 'unsafe-eval'.
В дополнение к этому, есть еще 'nonce-xxx', что означает генерацию случайной строки на бэкенде, например, a2b5zsa19c. Тогда можно загрузить тег скрипта с nonce=a2b5zsa19c:
Есть также похожее правило 'sha256-abc...', которое разрешает определенные встроенные скрипты на основе их хэша. Например, если мы возьмем alert(1) и рассчитаем его sha256-хэш, мы получим двоичное значение, которое при кодировании base64 становится bhHHL3z2vDgxUt0W3dWQOrprscmda2Y5pLsLg4GF+pI=. Поэтому в приведенном ниже примере будет загружен только скрипт с точным содержимым alert(1), в то время как другие не будут загружены:
Наконец, существует еще одно, которое может быть использовано, 'strict-dynamic', что означает: "Scripts that comply with the rules can load other scripts without being restricted by CSP." Вот так:
В установленном нами CSP для скриптов разрешается только nonce-rjg103rj1298e, и не разрешаются никакие другие источники. Однако скрипты, добавленные из <script nonce=rjg103rj1298e>, не ограничены и могут динамически добавлять скрипты из других источников. Это функция 'strict-dynamic'.
Как определить правила CSP?
При настройке CSP обычно начинают с default-src 'self', что позволяет по умолчанию использовать ресурсы с того же источника.
Теперь давайте обработаем наиболее важные скрипты. Обычно наиболее приоритетной задачей является избегание использования 'unsafe-inline' и 'unsafe-eval', так как наличие этих двух параметров не имеет большого значения по сравнению с отсутствием CSP вообще.
В чем состоит цель добавления CSP? Его цель - служить второй линией обороны против XSS-атак. Однако, если добавить 'unsafe-inline', это подрывает всю защиту, так как простая вставка <svg onload=alert(1)> может выполнить код.
Но в реальности обычно есть некоторые существующие встроенные скрипты, которые заставляют нас добавить 'unsafe-inline'. Здесь я представлю общий подход. Например, с Google Analytics они попросят вас добавить следующий код на вашу веб-страницу:
1. Добавить nonce в этот конкретный скрипт. 2. Вычислить хэш этого скрипта и добавить такое правило как sha256-xxx.
Оба этих решения позволяют выполнить определенные встроенные скрипты без использования разрешения 'unsafe-inline'. Кроме того, в официальной документации также напоминается, что если мы хотим использовать функцию "Custom JavaScript Variable", мы должны включить 'unsafe-eval', чтобы она работала.
Хотя ранее говорилось, что плохо настроенный CSP подобен отсутствию такового, все же лучше иметь эту конфигурацию. В конце концов, важно сделать первый шаг. Многие компании могли не знать о CSP в прошлом, поэтому добавление его заслуживает похвалы, и улучшения можно внести постепенно.
В первой половине статьи упоминался директивный вызов "report-uri", который является очень грамотной функцией. Если CSP неправильно настроен, он может блокировать обычные ресурсы, вызывая сбои в работе веб-сайта или неработоспособность определенных функций. Это будет потерей.
Поэтому есть еще один заголовок под названием Content-Security-Policy-Report-Only, что означает, что вы можете установить CSP, но он не будет блокировать что-либо. Вместо этого он будет отправлять отчет на указанный URL, когда загружается ресурс, нарушающий правила.
С помощью этой функции мы можем наблюдать любые нарушения CSP и проверять наличие неправильных настроек, изучая логи. Как только все подтверждено как должное, мы можем переключиться на использование Content-Security-Policy.
Как другие настраивают CSP?
Видели ли вы когда-нибудь длинную строку CSP?
Давайте посмотрим на CSP на главной странице GitHub, чтобы получить представление о том, как выглядит длинная строка:
Теперь давайте посмотрим на Facebook:
Хотя это тоже длинная строка, вы можете заметить, что для скриптов разрешено использование 'unsafe-inline', что является менее безопасным подходом. Если вы вставите эту CSP в CSP Evaluator, упомянутую выше, он покажет много красных флагов:
Заключение
Я лично рекомендую использование CSP. Как только она будет настроена, это добавляет дополнительный уровень защиты, давая нам возможность уменьшить любые проблемы. Блокируя XSS через CSP, мы можем минимизировать ущерб.
Кроме того, порог вступления в настройку CSP не высок. Вы можете начать с режима "report only", наблюдать и корректировать правила CSP для вашего сайта, и убедиться, что это не влияет на обычных пользователей, прежде чем запустить его в живую.
Наконец, давайте проведем небольшой викторину, на которую будет дан ответ в будущих разделах.
Last updated