Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom nonce values or modifying script tag attributes #756

Open
justinmacleod opened this issue Sep 29, 2020 · 9 comments
Open

Custom nonce values or modifying script tag attributes #756

justinmacleod opened this issue Sep 29, 2020 · 9 comments
Labels
need info Need more information on the issue

Comments

@justinmacleod
Copy link

Question

I need to supply a custom nonce tag that we generate ourselves due to the fact that we can not have multiple CSP headers for script-src being applied. Although multiple headers are valid, they do not get merged together and additional headers are only able to further restrict the policy. As such, we wish to generate the nonce ourselves and apply to our own inline scripts and then also use it with SEOmatic. However, I am unsure how to go about this.

In reviewing old issues, I came across #457 where it is mentioned that the script tag itself can be modified within the Tracking Scripts section, however, this doesn't appear to be the case with GTM. For some, I see the <script> tag is being defined here but for GTM it must be coming from else where. If you include the opening and closing script tags it results in the tag being duplicated.

  1. Is there an ability to override the nonce value?
  2. Is it possible to modify the script output?
  3. Is it possible to obtain just the values defined in the "Script Template" field so we can output on our own?

Cheers
Justin

@justinmacleod justinmacleod added the question Further information is requested label Sep 29, 2020
@khalwat
Copy link
Collaborator

khalwat commented Sep 30, 2020

You should be able to do this by doing something like (untested):

{% do seomatic.script.get("googleTagManager").nonce("myNonce") %}

...and the rest should just work from there.

As for your third question... if you mean the template that's used to render the GTM script, that's an editable Twig template that is on the SEOmatic -> Tracking Scripts -> Google Tag Manager page

@khalwat khalwat added need info Need more information on the issue and removed question Further information is requested labels Sep 30, 2020
@justinmacleod
Copy link
Author

That totally works. I swore I tried that but I guess not.

For the third question, I was thinking more of the rendered code so we could grab that and inject it into our own script tag but that is totally unnecessary. I will use the nonce param to be able to set our own value which gets replaced by Nginx on output using this approach. This allows for unique values on each request even if the data is cached by SEOmatic or Blitz.

Thanks for your help

@roelvanhintum
Copy link

@khalwat the nonce seems to be picked up by the template cache. Do you know how i can prevent this?

I used it with the craft csp plugin:

{% do seomatic.script.get("googleAnalytics").nonce(cspNonce('script-src')) %}

@khalwat
Copy link
Collaborator

khalwat commented Dec 31, 2020

@roelvanhintum can you clarify what you mean? What exactly is being cached?

@roelvanhintum
Copy link

@khalwat The passed nonce is cached with the script tag, which means the next fetch has an old nonce which doesn't match the CSP headers.

@khalwat khalwat reopened this Jan 4, 2021
@khalwat
Copy link
Collaborator

khalwat commented Apr 7, 2021

@roelvanhintum has this been sorted?

@roelvanhintum
Copy link

@khalwat unfortunately not. Looking back, i did not explained that i had to do some changes to the analytics script, because it injects a script tag. I've added the nonce also inside the script, but both are cached.

The modified template:

{% if trackingId.value is defined and trackingId.value %}
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.nonce="{{seomatic.script.get("googleAnalytics").nonce}}";a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','{{ analyticsUrl.value }}','ga');
ga('create', '{{ trackingId.value |raw }}', 'auto'{% if linker.value %}, {allowLinker: true}{% endif %});
{% if ipAnonymization.value %}
ga('set', 'anonymizeIp', true);
{% endif %}
{% if displayFeatures.value %}
ga('require', 'displayfeatures');
{% endif %}
{% if ecommerce.value %}
ga('require', 'ecommerce');
{% endif %}
{% if enhancedEcommerce.value %}
ga('require', 'ec');
{% endif %}
{% if enhancedLinkAttribution.value %}
ga('require', 'linkid');
{% endif %}
{% if enhancedLinkAttribution.value %}
ga('require', 'linker');
{% endif %}
{% set pageView = (sendPageView.value and not seomatic.helper.isPreview) %}
{% if pageView %}
ga('send', 'pageview');
{% endif %}
{% endif %}

Output:

<script nonce="246db853055fab1a4a807f4fb14e596ef0c11bbf5b83">(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]\|\|function(){
--
  | (i[r].q=i[r].q\|\|[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  | m=s.getElementsByTagName(o)[0];a.async=1;a.nonce="246db853055fab1a4a807f4fb14e596ef0c11bbf5b83";a.src=g;m.parentNode.insertBefore(a,m)
  | })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
  | ga('create', 'GA_KEY_GOES_HERE', 'auto');
  | ga('send', 'pageview');
  | </script>

Note that both nonces are cached.

@khalwat
Copy link
Collaborator

khalwat commented Apr 8, 2021

@roelvanhintum so I can think of ways around this, but I'm starting to believe that instead of a nonce, I think I should be using a hash:

https://content-security-policy.com/hash/

@roelvanhintum
Copy link

Good point, guess i need to do something similar for the csp plugin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
need info Need more information on the issue
Projects
None yet
Development

No branches or pull requests

3 participants