DevGang
Авторизоваться

Использование заголовков политики безопасности содержимого в React & emotion

Заголовки Content Security Policy (CSP) добавляют еще один уровень безопасности, запрещая небезопасные действия, такие как установление соединений с произвольными доменами, использование eval, inline-скриптов и др. В данной статье речь пойдет о директиве style-src и ее использовании с emotion.

Использование CSP-заголовков

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

Content-Security-Policy: style-src self;

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

  • self - стили, обслуживаемые с одного домена
  • URL, например, https://example.test
  • unsafe-eval - динамическое создание таблиц стилей из строк, например, с помощью CSSStyleSheet.insertRule()
  • unsafe-inline - инлайн-теги <style></style>
  • nonce-<value>, например, nonce-abc - позволяет создавать теги инлайн-стилей только с указанным значением атрибута nonce. Значение должно быть получено от криптографически защищенного генератора случайных токенов и пересоздаваться при каждом запросе.

Изменение стилей элементов с помощью DOM API, не включающих разбор CSS, разрешено, если выполнение JS не заблокировано директивой script-src. Для этого подойдет следующее:

element.style.display = "none";

Следующие действия не будут работать, если не включена функция unsafe-eval:

element.setAttribute("style", "display: none;");

CSP-заголовки и emotion

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

  1. unsafe-inline. Этот вариант не требует изменения кода фронтенда, но не дает никаких преимуществ в плане безопасности.
  2. nonce-<value>. Эта опция может быть использована для того, чтобы разрешить использование в приложении только инлайн-тегов стиля, созданных emotion. Это наиболее безопасный подход к работе с emotion. Чтобы он работал, emotion должен знать о значении nonce, установленном в заголовках ответа страницы. Точная конфигурация зависит от того, как используется emotion в вашем приложении.

Если вы используете @emotion/react или @emotion/styled, то вам необходимо предоставить собственный кэш с установленным nonce:

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}>
      ...
    </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(),
});

Обратите внимание, что при таком подходе необходимо изменить все места, где ранее использовался @emotion/css, на импорт модуля, в котором вызывается createEmotion:

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

Передача значения nonce в фронтенд

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

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

В дальнейшем фронтенд может использовать его следующим образом:

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

Обратите внимание на наличие атрибута пользовательского типа у тега script. Он необходим для того, чтобы браузер игнорировал тело скрипта и не выполнял его, что не работает при определенных значениях директивы script-src.

Спасибо за прочтение! Счастливого кодинга!

Источник:

#Security #React
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу