Content Security Policy в Magento 2

    Всем привет!

    Наверняка вы, установив Magento 2.3.5 обнаружили в своих браузерных консолях что-то вроде

    [Report Only] Refused to load the script '***' because it violates the following Content Security Policy directive: «script-src *». Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.



    О том, что делать, расскажу под катом

    Введение


    Начиная с версии 2.3.5 в Magento появился модуль под названием Magento_CSP. Он отвечает за Content Security Policy, и, собственно, добавляет заголовок Content-Security-Policy, а точнее, пока Content-Security-Policy-Report-Only. Как всегда вовремя и совершенно ожидаемо, при поднятии «фикс» версии :)



    О том, что такое Content Security Policy и с чем его едят, уже писали на Хабре.

    Выбор заголовка Content-Security-Policy или Content-Security-Policy-Report-Only происходит в зависимости от конфиг файла vendor/magento/module-csp/etc/config.xml отдельно для фронт отдельно для админ части.

    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
        <default>
            <csp>
                <mode>
                    <storefront>
                        <report_only>1</report_only>
                    </storefront>
                    <admin>
                        <report_only>1</report_only>
                    </admin>
                </mode>
            </csp>
        </default>
    </config>
    

    Там же в директиве «report_uri» можно было бы задать урл для репорта, но т.к. его там нет, то хром нещадно краснит консоль сообщением об его отсутствии.



    Примерный текст ошибки
    The Content Security Policy 'font-src 'self' 'unsafe-inline'; form-action secure.authorize.net test.authorize.net geostag.cardinalcommerce.com geo.cardinalcommerce.com 1eafstag.cardinalcommerce.com 1eaf.cardinalcommerce.com centinelapistag.cardinalcommerce.com centinelapi.cardinalcommerce.com 'self' 'unsafe-inline'; frame-ancestors 'self' 'unsafe-inline'; frame-src secure.authorize.net test.authorize.net geostag.cardinalcommerce.com geo.cardinalcommerce.com 1eafstag.cardinalcommerce.com 1eaf.cardinalcommerce.com centinelapistag.cardinalcommerce.com centinelapi.cardinalcommerce.com www.paypal.com www.sandbox.paypal.com 'self' 'unsafe-inline'; img-src widgets.magentocommerce.com www.googleadservices.com www.google-analytics.com t.paypal.com www.paypal.com www.paypalobjects.com fpdbs.paypal.com fpdbs.sandbox.paypal.com *.vimeocdn.com s.ytimg.com 'self' 'unsafe-inline'; script-src assets.adobedtm.com secure.authorize.net test.authorize.net geostag.cardinalcommerce.com 1eafstag.cardinalcommerce.com geoapi.cardinalcommerce.com 1eafapi.cardinalcommerce.com songbird.cardinalcommerce.com includestest.ccdc02.com js.authorize.net jstest.authorize.net www.googleadservices.com www.google-analytics.com www.paypal.com www.sandbox.paypal.com www.paypalobjects.com t.paypal.com js.braintreegateway.com s.ytimg.com video.google.com vimeo.com www.vimeo.com cdn-scripts.signifyd.com www.youtube.com 'self' 'unsafe-inline' 'unsafe-eval'; style-src getfirebug.com 'self' 'unsafe-inline'; object-src 'self' 'unsafe-inline'; media-src 'self' 'unsafe-inline'; manifest-src 'self' 'unsafe-inline'; connect-src geostag.cardinalcommerce.com geo.cardinalcommerce.com 1eafstag.cardinalcommerce.com 1eaf.cardinalcommerce.com centinelapistag.cardinalcommerce.com centinelapi.cardinalcommerce.com 'self' 'unsafe-inline'; child-src 'self' 'unsafe-inline'; default-src 'self' 'unsafe-inline' 'unsafe-eval'; base-uri 'self' 'unsafe-inline';' was delivered in report-only mode, but does not specify a 'report-uri'; the policy will have no effect. Please either add a 'report-uri' directive, or deliver the policy via the 'Content-Security-Policy' header.

    Как добавить свой урл в полиси?


    Создаем файлик в корне папки /etc/ модуля с названием csp_whitelist.xml и контентом

    <?xml version="1.0" encoding="UTF-8"?>
    <csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Csp:etc/csp_whitelist.xsd">
        <policies>
            <policy id="POLICY_ID">
                <values>
                    <value id="VALUE_ID" type="TYPE" algorithm="ALGORITHM">SOME DOMAIN</value>
                </values>
            </policy>
        </policies>
    </csp_whitelist>
    

    где POLICY_ID один из:

    • default-src
    • script-src
    • object-src
    • style-src
    • img-src
    • media-src
    • frame-src
    • font-src
    • connect-src

    VALUE_ID — произвольное уникальное имя
    TYPE — тип, может принимать значения domain или hash
    ALGORITHM — алгоритм хэширования (при TYPE=hash), например sha256

    Давайте посмотрим примеры из интеграционного теста самой Magento:

    <?xml version="1.0"?>
    <!--
    /**
     * Copyright  Magento, Inc. All rights reserved.
     * See COPYING.txt for license details.
     */
    -->
    <csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Csp/etc/csp_whitelist.xsd">
        <policies>
            <policy id="object-src">
                <values>
                    <value id="mage-base" type="host">https://magento.com</value>
                    <value id="hash" type="hash" algorithm="sha256">B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8=</value>
                    <value id="hash2" type="hash" algorithm="sha256">B3yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8=</value>
                </values>
            </policy>
            <policy id="media-src">
                <values>
                    <value id="mage-base" type="host">https://magento.com</value>
                    <value id="devdocs-base" type="host">https://devdocs.magento.com</value>
                </values>
            </policy>
        </policies>
    </csp_whitelist>
    
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 3

      0
      Доброго времени суток,

      сегодня накатил свежий 2.3.5-p1, config.xml уже выглядит так:
      config.xml
      <?xml version="1.0"?>
      <!--
      /**
       * Copyright © Magento, Inc. All rights reserved.
       * See COPYING.txt for license details.
       */
      -->
      <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
          <default>
              <csp>
                  <mode>
                      <storefront>
                          <report_only>1</report_only>
                      </storefront>
                      <admin>
                          <report_only>1</report_only>
                      </admin>
                  </mode>
                  <policies>
                      <storefront>
                          <base>
                              <policy_id>base-uri</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </base>
                          <default>
                              <policy_id>default-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>1</eval>
                              <dynamic>0</dynamic>
                          </default>
                          <children>
                              <policy_id>child-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </children>
                          <connections>
                              <policy_id>connect-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </connections>
                          <manifests>
                              <policy_id>manifest-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </manifests>
                          <media>
                              <policy_id>media-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </media>
                          <objects>
                              <policy_id>object-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </objects>
                          <styles>
                              <policy_id>style-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </styles>
                          <scripts>
                              <policy_id>script-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>1</eval>
                              <dynamic>0</dynamic>
                          </scripts>
                          <images>
                              <policy_id>img-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </images>
                          <frames>
                              <policy_id>frame-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </frames>
                          <frame-ancestors>
                              <policy_id>frame-ancestors</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </frame-ancestors>
                          <forms>
                              <policy_id>form-action</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </forms>
                          <fonts>
                              <policy_id>font-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </fonts>
                      </storefront>
                      <admin>
                          <base>
                              <policy_id>base-uri</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </base>
                          <default>
                              <policy_id>default-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>1</eval>
                              <dynamic>0</dynamic>
                          </default>
                          <children>
                              <policy_id>child-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </children>
                          <connections>
                              <policy_id>connect-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </connections>
                          <manifests>
                              <policy_id>manifest-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </manifests>
                          <media>
                              <policy_id>media-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </media>
                          <objects>
                              <policy_id>object-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </objects>
                          <styles>
                              <policy_id>style-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </styles>
                          <scripts>
                              <policy_id>script-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>1</eval>
                              <dynamic>0</dynamic>
                          </scripts>
                          <images>
                              <policy_id>img-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </images>
                          <frames>
                              <policy_id>frame-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </frames>
                          <frame-ancestors>
                              <policy_id>frame-ancestors</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </frame-ancestors>
                          <forms>
                              <policy_id>form-action</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </forms>
                          <fonts>
                              <policy_id>font-src</policy_id>
                              <self>1</self>
                              <inline>1</inline>
                              <eval>0</eval>
                              <dynamic>0</dynamic>
                          </fonts>
                      </admin>
                  </policies>
              </csp>
          </default>
      </config>
                                                                    


      и ошибка отсутствует.
        0
        Эта вся инфа есть и на официальном сайте. Все сторонние скрипты добавить получилось осталось пара моментов. Как добавить возможность использования например такого кода —

        background: url(data:image/svg+xml;base64,............);
        или
        @font-face{font-family:«Foo Bar»;src:url('data:font/woff;base64,d0............);

          0

          Насколько я понял, в патче 2.3.5-p1 прописали директивы только для самой Magento. Все сторонние модули и кастомный контент всё так же "краснят" консоль сообщениями об ошибках. Сделал модуль, который принимает CSP-отчёты от браузера и автоматом генерит правила для добавления к CSP-заголовку — https://github.com/flancer32/mage2_ext_csp


          Автоматом отслеживает нарушения полиси и генерирует соотв. правила. Убирает большинство ошибок с консоли. Но никакой аналитики там нет, поэтому правил получается довольно много (напрмер, img-src ws.sharethis.com & img-src l.sharethis.com можно было бы объединить в img-src *.sharethis.com).


          Так как CSP — это больше для девелоперов/админов, чем для конечных пользователей, то аналитика особо и не нужна. Мы можем и через SQL-запросы правила отредактировать. С той же img-src data: решается добавлением вручную соответствующего правила:


          INSERT INTO fl32_csp_rule (enabled, admin_area, type_id, source) VALUES(1, 0, 9, 'data:');

          Там нюанс, что в отчётах data просто идёт, а в правиле должно быть двоеточие в конце — data:. Ну, в общем, т.к. я этот инструмент под себя делал, то поэтому без "напильника" никак. Но, может, кому и пригодится.

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

          Самое читаемое