Adobe Commerce: Inline JavaScript Issues on checkout page in Content Security Policy (CSP) restricted mode

This article provides detailed explanations and solutions for issues encountered with custom JavaScript added via Adobe Commerce Admin and Google Tag Manager in Adobe Commerce 2.4.7 during checkout when CSP restricted mode is enabled. Specifically, it addresses the Refused to execute inline script because it violates the following Content Security Policy directiv e error message that appears in the browser console log. This error indicates that the inline script is blocked due to the stringent CSP settings, which are designed to enhance security by preventing the execution of unauthorized scripts.

Starting from Adobe Commerce 2.4.7, CSP is configured to operate in restrict-mode by default for payment pages in the storefront and admin areas. For all other pages, it operates in report-only mode. This enhancement necessitates the whitelisting of all JavaScript, including custom integrations with third-party services or extensions. Failure to whitelist custom JavaScript results in the browser blocking the execution of those scripts on checkout and payment pages in both the Admin and Storefront areas.

The solutions provided in this article are not limited to resolving issues with Google Tag Manager (GTM) Inline JavaScript or JavaScript added via the Design Configuration of Commerce Admin. They can also be applied to other scenarios where inline JavaScript has been added to the Commerce code. This includes custom scripts embedded directly within templates, modules, or any other part of the Adobe Commerce ecosystem. By following the outlined steps, you can ensure that all inline scripts are properly whitelisted and allowed to execute, thereby maintaining the functionality of your custom code while adhering to the CSP restrictions.

NOTE: It is highly recommended to introduce new JavaScript via methods described in the Content Security Policies Adobe Commerce documentation. These methods ensure that your scripts comply with CSP guidelines, enhancing the security of your Commerce site. By following best practices for script inclusion, such as using external scripts with proper nonce or hash attributes, you can minimize the risk of security vulnerabilities and ensure a smoother, more secure user experience.

Description description

Review for details on environment and steps to reproduce.

Environment

Adobe Commerce on cloud infrastructure and Adobe Commerce on-premises:

  • 2.4.7 and higher
  • 2.4.6-pX
  • 2.4.5-pX
  • 2.4.4-pX

Issue/Symptoms

Below is a list of common issues and their solutions when scripts are blocked from executing on checkout and payment pages due to CSP restrictions:

  • GTM HTML Tag with Inline JavaScript
  • Inline JS in Theme Configuration

GTM HTML Tag with Inline JavaScript

The JavaScript from the Custom HTML Tag configured in Google Tag Manager is not executing properly on the storefront checkout or payment pages.

Steps to Reproduce

  1. Configure Google Tag Manager with a custom HTML tag that contains inline JavaScript.
  2. Integrate Google Tag Manager with Adobe Commerce. Refer to Configure your Google Analytics account in the Adobe Commerce Merchandising and Promotions Guide, for steps.
  3. Add a product to the cart and proceed to checkout.
  4. Open the Developer Console in any supported browser.

Expected Results

No errors related to the custom JavaScript appear in the console, and the script executes successfully.

Actual Results

The error Refused to execute a script because its hash, its nonce, or ‘unsafe-inline’ does not appear in the script-src directive of the Content Security Policy. is present in the console, and the script does not execute.

NOTE: The exact error message can vary depending on the browser, but it generally indicates that the script is blocked by the CSP. These messages highlight that the script is not permitted to run due to the current CSP settings.

Cause

The JavaScript from the Google Tag Manager Custom HTML Tag is injected into the storefront by Google Tag Manager itself. As a result, this script is not pre-whitelisted in the CSP settings and is subsequently blocked from execution by the browser. This occurs because the CSP restricts the execution of any inline scripts that are not explicitly allowed, ensuring enhanced security but requiring additional configuration for custom scripts.

Solution

  • Whitelist the JavaScript Hash. Refer to the Resolution section in this article for details.
  • Sign Google Tag Manager Custom HTML JavaScript with a Nonce. Refer to the Resolution section in this article for details.

Inline JS in Theme Configuration

This issue is very similar to the Custom HTML Tag with Inline JavaScript issue. The difference is that instead of adding the JavaScript in the Google Tag Manager Admin, the script is added in the Adobe Commerce Admin at the Design Configuration Page for one of the available scopes. Using this method, an inline HTML snippet, JavaScript, or stylesheet can be added to the header or footer of the theme. Just like any other inline JavaScript, it will require whitelisting to be executed on the checkout page.

Steps to Reproduce

  1. ConfigureHTML Head or Footer in Design Configuration to contain an inline JavaScript.
  2. Add a product to the cart and proceed to checkout.
  3. Open the Developer Console in any supported browser.

Expected Results

No errors related to the custom JavaScript appear in the console, and the script executes successfully.

Actual Results

The error Refused to execute a script because its hash, its nonce, or ‘unsafe-inline’ does not appear in the script-src directive of the Content Security Policy. is present in the console, and the script does not execute.

NOTE: The exact error message can vary depending on the browser, but it generally indicates that the script is being blocked by the CSP. These messages highlight that the script is not permitted to run due to the current CSP settings.

Cause

Scripts and Style Sheets in the HTML Head and Miscellaneous HTML in the Footer sections of the Design Configuration are mixed input fields. These fields can contain HTML, Style Sheets, or JavaScript. Due to this dynamic content, it is impossible to hash and whitelist the content of those fields automatically. Therefore, if JavaScript is added to either of these fields, it must be manually whitelisted to be executed on the checkout page. This is necessary because the CSP restricts the execution of any inline scripts that are not explicitly allowed. While this ensures enhanced security, it also requires additional configuration to permit custom scripts.

Solution

Whitelist the JavaScript Hash. Refer to the Resolution section in this article for details.

Resolution resolution

Each provided solution operates independently. Carefully evaluate and select the one that best addresses your specific needs. Consider the context of your implementation, the nature of the scripts involved, and the security requirements of your Adobe Commerce site to determine the appropriate solution.

Whitelist the JavaScript Hash

To resolve this issue, the custom inline JavaScripts must be whitelisted in the CSP settings. This ensures that the script is explicitly allowed to execute, bypassing the default security restrictions.

Whitelisting GTM custom HTML scripts is challenging because GTM may modify the JavaScript before injecting it into the Document Object Model (DOM), including removing line breaks and comments. Additionally, Google’s algorithms may change over time without notice, potentially invalidating the hash. You need to use the hash generated by Google Chrome as described in step C and be prepared to update the hash in your whitelist periodically. Alternatively, consider signing Google Tag Manager Custom HTML JavaScript with a Nonce for a more robust solution.

  1. Generate the hash for the JavaScript body.

    NOTE: To generate the hash successfully, you need to feed the script into the hash generator. It is important to copy the script carefully. Exclude the opening and closing script tags of the JavaScript, while copying all line breaks and any possible invisible characters. This includes the line breaks (if any) after the opening script or other tags. If the hash does not match the script exactly, execution is denied.

    1. On a Mac, you can copy the entire script body, including any line breaks after the opening script tag, to the clipboard, and execute the following command in the terminal.

      php -r “echo base64_encode(hash(‘sha256’, shell_exec(‘pbpaste’), true)) . PHP_EOL;”

      This PHP command takes the clipboard contents, computes its SHA-256 hash, converts the hash to binary, and then encodes it in base64 format, finally printing the result.

    2. You may use a variety of online hash generators to create the required hash for your script.

      WARNING:  It is crucial to understand that if you decide to use third-party online services to generate hashes for CSP, you must consider the privacy implications. Some services may upload your script to their servers for hashing, potentially compromising sensitive data included in your script. To mitigate this risk, it is recommended to generate hashes locally using trusted tools or scripts, ensuring that your data remains secure and private.

    3. You can use the Google Chrome browser to acquire the already generated hash for the JavaScript that was denied execution on the checkout page from the Developer Console.

      1. Go to the checkout page using the Google Chrome browser with the blocked JavaScript added.

      2. Open the Developer Console by pressing Cmd+Option+J (on macOS) or Ctrl+Shift+J (on Windows/Linux).

      3. Locate the CSP error message in the console.

      4. In the last sentence of the error message, you find the generated hash code for the blocked script.

      5. Copy the code aftersha256-, omitting the quotation marks.

        NOTE:  If you have multiple blocked JavaScript files, you see multiple error messages in the console. Ensure you identify the exact JavaScript that needs to be whitelisted. It is advisable to add and test each JavaScript file one by one to avoid mistakenly whitelisting the wrong script.

        For more details on how to generate a hash for inline JavaScript, refer to the Advanced CSP Configuration in the Adobe Commerce Developer Content Security Policies guide.

  2. Whitelist the Script Hash. First Add a hash to your module’s csp_whitelist.xml file:

    < values>

    < value id=“my-script” type=“hash” algorithm=“sha256”> YOUR-HASH-1< /value>

    < /values>

    Where YOUR-HASH-1 should be replaced with the hash you acquired in the previous step.
    To whitelist multiple scripts, add a < value> < /value> tag for each script, for example:

    < values>

    < value id=“my-script” type=“hash” algorithm=“sha256”> YOUR-HASH-1< /value>

    < value id=“my-new-script” type=“hash” algorithm=“sha256”> YOUR-HASH-2< /value>

    < values>

    If the file does not exist, create it with the following content.

    < ?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=“script-src”>

    < values>

    < value id=“my-script” type=“hash” algorithm=“sha256”> YOUR-HASH-1< /value>

    < value id=“my-new-script” type=“hash” algorithm=“sha256”> YOUR-HASH-2< /value>

    < /values>

    < /policy>

    < /policies>

    < /csp_whitelist>

  3. Flush the cache: After adding the hash to the csp_whitelist.xml file, it is essential to flush the cache to ensure that the changes take effect. Flushing the cache clears the stored data, allowing the updated CSP settings to be applied immediately. You can flush the cache by navigating to System > Tools > Cache Management in the Commerce admin panel and select the Flush Magento Cache button.  Alternatively, use the command line:

    bin/magento cache:flush

    This command clears all cache types, ensuring that your new CSP settings are recognized by the system.

Sign Google Tag Manager Custom HTML JavaScript with a Nonce

Another way to allow the execution of JavaScript in GTM is by adding a nonce to the script’s opening tag. The nonce attribute provides a way to whitelist specific inline scripts dynamically, ensuring they are permitted to execute. For more details, refer to Using CSP nonce provider to allow inline scripts documentation.

WARNING:  Keep in mind that if the GTM account is compromised, an attacker can inject malicious JavaScript into the storefront and sign it with the nonce, allowing its execution. This could potentially lead to the theft of sensitive data during the checkout process.

Adobe Commerce Development Part

NOTE: CSP Nonce Variable injection will be available out of the box in Adobe Commerce 2.4.8 and later versions. If you implement this custom injection in earlier versions of Adobe Commerce, please roll back these customizations before upgrading to Adobe Commerce 2.4.8 or higher. If you are running Adobe Commerce 2.4.8 or higher, please proceed to the GTM Configuration section.

  1. In your custom module, utilize CSP Nonce Provider and pass the nonce to the JavaScript. For more details, refer to Basic Template Concepts in the Adobe Commerce Developer documentation.

  2. Inject the global variable with the nonce using JavaScript:

    < script>

    window.cspNonce = config.cspNonce;

    < /script>

  3. This script sets a global variable cspNonce with the value of the current nonce, which can then be captured in Google Tag Manager variable and used to sign Custom HTML scripts to ensure they are allowed to execute under the CSP. It should be injected to all pages.

GTM Configuration Part

  1. Capture the value of this variable from GTM:

    1. Create a Google Tag Manager Variable of the type JavaScript Variable. Give the variable a clear name, as it will be referenced later. In this example, it’s gtmNonce.

    2. Set the Global Variable Name to the name of the JavaScript global variable injected in the previous step. In this example, it’s cspNonce.

  2. Modify your Custom HTML block that contains the JavaScript you need to execute on checkout to include the nonce attribute, referencing the GTM Variable you created earlier.

< script nonce=“{{gtmNonce}}”>
                     console.log(“This is a test”);
              < /script>

NOTE: Ensure that you check the Support document.write checkbox, as this is essential for the script to function correctly.

By adding the nonce attribute, the script is signed with the provided nonce, allowing it to execute securely under the Content Security Policy (CSP).

recommendation-more-help
3d58f420-19b5-47a0-a122-5c9dab55ec7f