# Вторая линия обороны от XSS - CSP (Content Security Policy)

Первая линия обороны от XSS - это обработка и санитизация пользовательского ввода для обеспечение безопасности содержимого. Однако это проще сказать, чем сделать, особенно для устаревших проектов с запутанным и сложным кодом. Становится сложно определить, где следует внести необходимые исправления.

Кроме того, ошибки могут произойти при написании кода, и есть три основные причины возникновения проблем безопасности:

1\. Вы не знаете, что определенные действия могут привести к проблемам.\
2\. Вы забываете, что определенные действия могут привести к проблемам.\
3\. Вы знаете, что определенные действия могут привести к проблемам, но из-за сроков выполнения проекта или указаний руководства, вы предпочитаете их игнорировать.

Первая причина аналогична упомянутому ранее примеру с тегом **\<a href>**, в котором вы, возможно, не знаете, что он может исполнять код с помощью **javascript:**.

Вторая причина - когда вы знаете о уязвимостях **XSS** и знаете, что вывод должен быть закодирован, но забываете это сделать.

Третья причина - это когда вы осознанно оставляете уязвимость без исправления, хотя она должна быть исправлена, из-за ограничений по времени проекта или указаний руководства.

В случае с первой причиной, когда у вас нет представления о том, где следует решить проблему, или о наличии уязвимости, как можно защититься от нее? Вот здесь нам и понадобится **CSP**.

### **Автоматический механизм защиты: Content Security Policy** <a href="#id-vtorayaliniyaoboronyotxsscsp-contentsecuritypolicy-avtomaticheskiimekhanizmzashity-contentsecurit" id="id-vtorayaliniyaoboronyotxsscsp-contentsecuritypolicy-avtomaticheskiimekhanizmzashity-contentsecurit"></a>

**CSP**, сокращенно от **Content Security Policy**, позволяет вам устанавливать правила для вашей веб-страницы и информировать браузер, что разрешено только содержимое, которое соответствует этим правилам. Любой контент, который не соответствует, должен быть заблокирован.

Есть два **способа** добавления CSP на веб-страницу. Один через заголовок HTTP-ответа **Content-Security-Policy**, и другой через тег **\<meta>**. Поскольку последний способ проще для демонстрации, мы сосредоточимся на нем (хотя более часто используется первый, так как некоторые правила можно установить только через него).

(Существует еще один загадочный третий метод, связанный с атрибутом **csp** тега **\<iframe>**, но мы не будем здесь обсуждать это.)

Посмотрим на пример:

{% code overflow="wrap" %}

```html
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Security-Policy" content="script-src 'none'">
</head>
<body>
    <script>alert(1)</script>
    CSP test
</body>
</html>
```

{% endcode %}

На этой веб-странице 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** <a href="#id-vtorayaliniyaoboronyotxsscsp-contentsecuritypolicy-pravilacsp" id="id-vtorayaliniyaoboronyotxsscsp-contentsecuritypolicy-pravilacsp"></a>

**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:.

Итак, какие доступны директивы?

Самая важная - это **default-src**, которая представляет собой правила по умолчанию. Например, если **script-src** не установлен, будут использоваться правила из **default-src**. Однако важно заметить, что некоторые директивы не откатываются к **default-src**, такие как **base-uri** или **form-action**. Полный список можно найти здесь: [Директива default-src](https://content-security-policy.com/default-src/]\(https://content-security-policy.com/default-src/)

Другие часто используемые директивы включают:\
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** в конце - это более новая функция, которая еще не поддерживается современными браузерами.

В дополнение к этому, на самом деле существует еще больше директив, но я не упоминал менее распространенные. Если вас это интересует, вы можете обратиться к [MDN: Content-Security-Policy](https://developer.mozilla.org/ru/docs/Web/HTTP/Headers/Content-Security-Policy) и [Content Security Policy Reference](https://content-security-policy.com/) для получения дополнительной информации.

Так что какие возможны правила для каждого из них? В зависимости от директив могут использоваться разные правила.

Наиболее часто используемые правила таковы:

1\. **\*** - Разрешает все URL, кроме **data:**, **blob:** и **filesystem:**.\
2\. **'none'** - Ничего не разрешает.\
3\. **'self'** - Разрешает только ресурсы того же происхождения.\
4\. **https:** - Разрешает все ресурсы HTTPS.\
5\. [**example.com**](http://example.com/) - Разрешает определенные домены (и HTTP, и HTTPS).\
6\. [**https://example.com**](https://example.com/) - Разрешает определенные происхождения (только HTTPS).

Например, **script-src \*** по сути эквивалентно отсутствию какого-либо правила (разрешает все URL, но стоит отметить, что встроенный скрипт все равно блокируется), в то время как **script-src 'none'** полностью блокирует выполнение любого **JavaScript.**

Более того, некоторые правила можно объединять. На практике вы часто видите правила вроде этого:

```html
script-src 'self' cdn.example.com www.google-analytics.com *.facebook.net
```

Иногда скрипты размещаются на том же origin, поэтому необходимо self. Некоторые скрипты размещаются на CDN, поэтому требуется [cdn.example.com](http://cdn.example.com/). И если используются Google Analytics и Facebook SDK, требуется [www.google-analytics.com](http://www.google-analytics.com/) \*.[facebook.net](http://facebook.net/) для загрузки их JavaScript.

Полная CSP - это комбинация этих правил, с директивами, разделенными **;**, вот так:

{% code overflow="wrap" %}

```
default-src 'none'; script-src 'self' cdn.example.com www.google-analytics.com *.facebook.net; img-src *;
```

{% endcode %}

С помощью CSP мы можем сообщить браузеру, какие ресурсы разрешено загружать, а какие нет. Даже если атакующий найдет точку инъекции, он может не суметь выполнить JavaScript, таким образом, снижая воздействие уязвимости XSS (хотя ее все равно нужно исправить, но риск меньше).

### **Правила для script-src** <a href="#id-vtorayaliniyaoboronyotxsscsp-contentsecuritypolicy-praviladlyascript-src" id="id-vtorayaliniyaoboronyotxsscsp-contentsecuritypolicy-praviladlyascript-src"></a>

Помимо указания URL ресурсов для загрузки, существуют другие правила, которые можно использовать.

Например, после установки CSP, встроенные скрипты и eval блокируются по умолчанию. Заблокированы следующие встроенные скрипты:

1\. Код, прямо размещенный внутри тегов \<script> (должен быть загружен из внешнего источника с использованием \<script src>)\
2\. Обработчики событий, написанные в HTML, такие как onclick\
3\. Псевдопротокол javascript:

Чтобы разрешить использование встроенных скриптов, необходимо добавить правило **'unsafe-inline'**.

И если вы хотите выполнить код, как если бы он был **eval**, вам нужно добавить правило **'unsafe-eval'**. Некоторые люди могут знать, что **setTimeout** также может выполнять код в виде строки, вот так:

```javascript
setTimeout('alert(1)')
```

Аналогично, **setInterval**, **Function** и другие могут добиться того же, но все они требуют использования правила **'unsafe-eval'**.

В дополнение к этому, есть еще **'nonce-xxx'**, что означает генерацию случайной строки на бэкенде, например, **a2b5zsa19c**. Тогда можно загрузить тег скрипта с **nonce=a2b5zsa19c**:

{% code overflow="wrap" %}

```html
<!-- Allowed -->
<script nonce="a2b5zsa19c">
    alert(1);
</script>

<!-- Not allowed -->
<script>
    alert(1);
</script>
```

{% endcode %}

Есть также похожее правило **'sha256-abc...'**, которое разрешает определенные встроенные скрипты на основе их хэша. Например, если мы возьмем **alert(1)** и рассчитаем его sha256-хэш, мы получим двоичное значение, которое при кодировании base64 становится **bhHHL3z2vDgxUt0W3dWQOrprscmda2Y5pLsLg4GF+pI=**. Поэтому в приведенном ниже примере будет загружен только скрипт с точным содержимым **alert(1)**, в то время как другие не будут загружены:

{% code overflow="wrap" %}

```html
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Security-Policy" content="script-src 'sha256-bhHHL3z2vDgxUt0W3dWQOrprscmda2Y5pLsLg4GF+pI='">
</head>
<body>
    <!-- Allowed -->
    <script>alert(1)</script>
    
    <!-- Not Allowed -->
    <script>alert(2)</script>
    
    <!-- An extra space is also not allowed because it results in a different hash value -->
    <script>alert(1) </script>
</body>
</html>
```

{% endcode %}

Наконец, существует еще одно, которое может быть использовано, **'strict-dynamic'**, что означает: **"Scripts that comply with the rules can load other scripts without being restricted by CSP."** Вот так:

{% code overflow="wrap" %}

```html
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Security-Policy" content="script-src 'nonce-rjg103rj1298e' 'strict-dynamic'">
</head>
<body>
    <script nonce="rjg103rj1298e">
        const element = document.createElement('script');
        element.src = 'https://example.com';
        document.body.appendChild(element);
    </script>
</body>
</html>
```

{% endcode %}

В установленном нами CSP для скриптов разрешается только **nonce-rjg103rj1298e**, и не разрешаются никакие другие источники. Однако скрипты, добавленные из **\<script nonce=rjg103rj1298e>**, не ограничены и могут динамически добавлять скрипты из других источников. Это функция **'strict-dynamic'**.

### **Как определить правила CSP?** <a href="#id-vtorayaliniyaoboronyotxsscsp-contentsecuritypolicy-kakopredelitpravilacsp" id="id-vtorayaliniyaoboronyotxsscsp-contentsecuritypolicy-kakopredelitpravilacsp"></a>

При настройке CSP обычно начинают с **default-src 'self'**, что позволяет по умолчанию использовать ресурсы с того же источника.

Теперь давайте обработаем наиболее важные скрипты. Обычно наиболее приоритетной задачей является избегание использования **'unsafe-inline'** и **'unsafe-eval'**, так как наличие этих двух параметров не имеет большого значения по сравнению с отсутствием CSP вообще.

В чем состоит цель добавления CSP? Его цель - служить второй линией обороны против XSS-атак. Однако, если добавить **'unsafe-inline'**, это подрывает всю защиту, так как простая вставка **\<svg onload=alert(1)>** может выполнить код.

Но в реальности обычно есть некоторые существующие встроенные скрипты, которые заставляют нас добавить **'unsafe-inline'.** Здесь я представлю общий подход. Например, с Google Analytics они попросят вас добавить следующий код на вашу веб-страницу:

{% code overflow="wrap" %}

```html
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-XXXXXXXX-X"></script>
<script>
    window.dataLayer = window.dataLayer || [];
    function gtag() {
        dataLayer.push(arguments);
    }
    gtag('js', new Date());
    gtag('config', 'UA-XXXXXXXX-X');
</script>
```

{% endcode %}

Это встроенный скрипт, которого мы хотим избежать. Итак, что мы можем сделать? Официальная документация, предоставляемая Google, [Использование инструмента управления кодом с политикой безопасности контента](https://developers.google.com/tag-platform/tag-manager/csp?hl=en), упоминает два решения:

1\. Добавить n**once** в этот конкретный скрипт.\
2\. Вычислить хэш этого скрипта и добавить такое правило как **sha256-xxx**.

Оба этих решения позволяют выполнить определенные встроенные скрипты без использования разрешения **'unsafe-inline'**. Кроме того, в официальной документации также напоминается, что если мы хотим использовать функцию **"Custom JavaScript Variable"**, мы должны включить **'unsafe-eval'**, чтобы она работала.

Если вы не уверены в безопасности ваших правил CSP, вы можете воспользоваться сайтом, предоставленным компанией Google под названием [CSP Evaluator](https://csp-evaluator.withgoogle.com/). Он обнаружит любые ошибки в вашей настройке CSP и оценит безопасность, как показано здесь:\\

<figure><img src="https://4101843849-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9aPqL1pJrSP93yQXPt21%2Fuploads%2FH6JpT2QBNKHB5FOxA4Z0%2F%D0%B8%D0%B7%D0%BE%D0%B1%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5.png?alt=media&#x26;token=67634f26-5d11-42d8-bcf0-f1b2d8207ba9" alt=""><figcaption></figcaption></figure>

Хотя ранее говорилось, что плохо настроенный **CSP** подобен отсутствию такового, все же лучше иметь эту конфигурацию. В конце концов, важно сделать первый шаг. Многие компании могли не знать о CSP в прошлом, поэтому добавление его заслуживает похвалы, и улучшения можно внести постепенно.

В первой половине статьи упоминался директивный вызов **"report-uri"**, который является очень грамотной функцией. Если **CSP** неправильно настроен, он может блокировать обычные ресурсы, вызывая сбои в работе веб-сайта или неработоспособность определенных функций. Это будет потерей.

Поэтому есть еще один заголовок под названием **Content-Security-Policy-Report-Only**, что означает, что вы можете установить **CSP**, но он не будет блокировать что-либо. Вместо этого он будет отправлять отчет на указанный URL, когда загружается ресурс, нарушающий правила.

С помощью этой функции мы можем наблюдать любые нарушения **CSP** и проверять наличие неправильных настроек, изучая логи. Как только все подтверждено как должное, мы можем переключиться на использование **Content-Security-Policy**.

### **Как другие настраивают CSP?** <a href="#id-vtorayaliniyaoboronyotxsscsp-contentsecuritypolicy-kakdrugienastraivayutcsp" id="id-vtorayaliniyaoboronyotxsscsp-contentsecuritypolicy-kakdrugienastraivayutcsp"></a>

Видели ли вы когда-нибудь длинную строку **CSP**?

Давайте посмотрим на CSP на главной странице GitHub, чтобы получить представление о том, как выглядит длинная строка:

{% code overflow="wrap" %}

```
default-src  'none';base-uri  'self'; child-src  github.com/assets-cdn/worker/  gist.github.com/assets-cdn/worker/;connect-src  'self'  uploads.github.com  objects-origin.githubusercontent.com  www.githubstatus.com  collector.github.com  raw.githubusercontent.com  api.github.com  github-cloud.s3.amazonaws.com  github-production-repository-file-5c1aeb.s3.amazonaws.com  github-production-upload-manifest-file-7fdce7.s3.amazonaws.com  github-production-user-asset-6210df.s3.amazonaws.com  cdn.optimizely.com  logx.optimizely.com/v1/events  *.actions.githubusercontent.com  productionresultssa0.blob.core.windows.net/  productionresultssa1.blob.core.windows.net/  productionresultssa2.blob.core.windows.net/  productionresultssa3.blob.core.windows.net/  productionresultssa4.blob.core.windows.net/  wss://*.actions.githubusercontent.com  github-production-repository-image-32fea6.s3.amazonaws.com  github-production-release-asset-2e65be.s3.amazonaws.com  insights.github.com  wss://alive.github.com github.githubassets.com; font-src  github.githubassets.com;form-action  'self'  github.com  gist.github.com  objects-origin.githubusercontent.com;frame-ancestors  'none';frame-src  viewscreen.githubusercontent.com  notebooks.githubusercontent.com;img-src  'self'  data:  github.githubassets.com  media.githubusercontent.com  camo.githubusercontent.com  identicons.github.com  avatars.githubusercontent.com  github-cloud.s3.amazonaws.com  objects.githubusercontent.com  objects-origin.githubusercontent.com  secured-user-images.githubusercontent.com/  user-images.githubusercontent.com/  private-user-images.githubusercontent.com  opengraph.githubassets.com  github-production-user-asset-6210df.s3.amazonaws.com  customer-stories-feed.github.com  spotlights-feed.github.com  *.githubusercontent.com;manifest-src  'self';media-src  github.com  user-images.githubusercontent.com/  secured-user-images.githubusercontent.com/  private-user-images.githubusercontent.com  github.githubassets.com;script-src  github.githubassets.com;style-src  'unsafe-inline'  github.githubassets.com;upgrade-insecure-requests;worker-src  github.com/assets-cdn/worker/  gist.github.com/assets-cdn/worker/
```

{% endcode %}

В основном, это включает в себя различные возможные конфигурации. Что касается скриптов, которые нас больше всего заботят, разрешено использование только [github.githubassets.com](http://github.githubassets.com/);, что является безопасным способом настройки.

Программа Bug Bounty GitHub также имеет специальную категорию, называемую [GitHub CSP](https://bounty.github.com/targets/csp.html). Если вы сможете обойти **CSP** и выполнить код, даже если вы не нашли место для внедрения HTML, это все равно будет учтено.

Теперь давайте посмотрим на Facebook:

{% code overflow="wrap" %}

```
default-src  *  data:  blob:  'self'  'wasm-unsafe-eval'script-src  *.facebook.com  *.fbcdn.net  *.facebook.net  *.google-analytics.com  *.google.com  127.0.0.1:*  'unsafe-inline'  blob:  data:  'self'  'wasm-unsafe-eval'style-src  data:  blob:  'unsafe-inline'  *connect-src  secure.facebook.com  dashi.facebook.com  dashi-pc.facebook.com  graph-video.facebook.com  streaming-graph.facebook.com  z-m-graph.facebook.com  z-p3-graph.facebook.com  z-p4-graph.facebook.com  rupload.facebook.com  upload.facebook.com  vupload-edge.facebook.com  vupload2.facebook.com  z-p3-upload.facebook.com  z-upload.facebook.com  graph.facebook.com  'self'  *.fbcdn.net  wss://*.fbcdn.net  attachment.fbsbx.com  blob:  data:  *.cdninstagram.com  *.up.facebook.com  wss://edge-chat-latest.facebook.com  wss://edge-chat.facebook.com  edge-chat.facebook.com  edge-chat-latest.facebook.com  wss://gateway.facebook.com  *.facebook.com/rsrc.php/  https://api.mapbox.com  https://*.tiles.mapbox.comblock-all-mixed-contentupgrade-insecure-requests;
```

{% endcode %}

Хотя это тоже длинная строка, вы можете заметить, что для скриптов разрешено использование **'unsafe-inline'**, что является менее безопасным подходом. Если вы вставите эту CSP в CSP Evaluator, упомянутую выше, он покажет много красных флагов:\\

<figure><img src="https://4101843849-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9aPqL1pJrSP93yQXPt21%2Fuploads%2FUoLi81V3jZ243jmkyJ1K%2F%D0%B8%D0%B7%D0%BE%D0%B1%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5.png?alt=media&#x26;token=9bda31e1-adc4-442b-9235-9e63a60dafa7" alt=""><figcaption></figcaption></figure>

### **Заключение** <a href="#id-vtorayaliniyaoboronyotxsscsp-contentsecuritypolicy-zaklyuchenie" id="id-vtorayaliniyaoboronyotxsscsp-contentsecuritypolicy-zaklyuchenie"></a>

Я лично рекомендую использование **CSP**. Как только она будет настроена, это добавляет дополнительный уровень защиты, давая нам возможность уменьшить любые проблемы. Блокируя XSS через CSP, мы можем минимизировать ущерб.

Кроме того, порог вступления в настройку **CSP** не высок. Вы можете начать с режима "report only", наблюдать и корректировать правила **CSP** для вашего сайта, и убедиться, что это не влияет на обычных пользователей, прежде чем запустить его в живую.

Наконец, давайте проведем небольшой викторину, на которую будет дан ответ в будущих разделах.

После прочтения этой статьи, Боб взглянул на свой проект и обнаружил, что все файлы JavaScript появляются из пакетов [https://unpkg.com](https://unpkg.com/). Поэтому он добавил следующую **CSP**. В чем проблема с частью **script-src**?

Content-Security-Policy: script-src [https://unpkg.com](https://unpkg.com/);
