Использование Content-Security-Policy вместе с React & Emotion

от автора

Content-Security-Policy (CSP) — это HTTP заголовок, который улучшает безопасность веб-приложений за счет запрета небезопасных действий, таких как загрузка и отправка данных на произвольные домены, использование eval, inline-скриптов и т.д. В этой статье будет сделан фокус на директиве style-src и ее использование вместе с CSS-in-JS библиотекой emotion.

Кратко о CSP и style-src

Content-Security-Policy заголовок должен быть выставлен в ответе вместе с загружаемой веб-страницей (например, index.html). Это выглядит следующим образом:

Content-Security-Policy: style-src 'self'

style-src — это директива, которая отвечает за то, какие стили можно загружать и применять на странице. Возможные значения:

  • 'none' — все стили запрещены

  • 'self' — разрешены файлы стилей, которые загружаются с того же домена, что и основной документ (страница)

  • <url> , например https://example.com — разрешены файлы стилей с этого домена, также допускаются wildcard (*) на месте под-домена и порта

  • '<hash-algorithm>-<base64-value>', например 'sha256-ozBpjL6dxO8fsS4u6fwG1dFDACYvpNxYeBA6tzR+FY8=' — разрешены файлы стилей и inline-стили (тег <style>), у которых хеш совпадает с указанным значением

  • 'nonce-<value>' , например 'nonce-abc' — разрешаются inline-стили, у которых атрибут nonce совпадает с указанным (в примере — abc)

  • 'unsafe-hashes' — разрешает inline-стили, указанные в атрибуте style строкой, например <div style="color:red;"></div>, при этом хеш значения атрибута должен совпадать с хешом, указанным в '<hash-algorithm>-<base64-value>'

  • 'unsafe-inline' — разрешает все inline-стили, созданные через тег <style>

  • 'unsafe-eval' — разрешает добавление/изменение CSS declarations, которые приводят к парсингу строки, например, с помощью CSSStyleDeclaration.cssText

Директива может принимать несколько значений через пробел. В этом случае это трактуется как логическое «или» — при удовлетворении хотя бы одному значению стили разрешаются.

CSP и emotion

emotion добавляет style элементы динамически и в последних версиях не может извлекать все стили в отдельный файл во время сборки приложения. Это означает, что для того, чтобы можно было использовать emotion вместе с style-src, есть следующие опции:

  1. 'unsafe-inline' — самая простая опция из всех. Не требует какой-либо настройки со стороны emotion. При этом мы снижаем безопасность нашего приложения, поэтому это решение можно использовать только как временное.

  2. 'nonce-<value>' — можно разрешить inline-стили, созданные emotion. Для этого нужно задать nonce при создании cache.

При использовании @emotion/react или @emotion/styled это можно сделать следующим образом:

import { CacheProvider } from "@emotion/react"; import createCache from "@emotion/cache";  export function App() {   const cache = createCache({     key: 'my-app',     nonce: getNonceValue(),   });      return (     <CacheProvider cache={cache}>       {/* children */}     </CacheProvider>   ); }

Если используется @emotion/css напрямую, то потребуется создать свой экземпляр emotion:

import createEmotion from '@emotion/css/create-instance';  export const {   flush,   hydrate,   cx,   merge,   getRegisteredStyles,   injectGlobal,   keyframes,   css,   sheet,   cache } = createEmotion({   key: 'my-app',   nonce: getNonceValue(), });

При использовании createEmotion потребуется поменять все места, где раньше импортировался @emotion/css на этот модуль:

// import { css } from "@emotion/css";  import { css } from "./emotion";

Передача nonce на фронтенд

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

<script id="nonce" type="application/json">   "abc" </script>

На фронтенде это можно использовать таким образом:

function getNonceValue() {   const nonceElement = document.getElementById("nonce");   return JSON.parse(nonceElement.textContent); }

Обратите внимание на type="application/json" — таким образом браузер не считает это исполняемым кодом, и особое значение для script-src не требуется.


ссылка на оригинал статьи https://habr.com/ru/articles/772100/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *