Problem/Motivation

The Content Security Policy module needs to add 'unsafe-inline' exceptions for scripts and styles in order to support AJAX responses (#3100068: Script/style included in AJAX responses blocked without 'unsafe-inline').
This is because CSP blocks script and style elements dynamically added to the page by appending HTML, even if their sources comply with the page's policy.
Since AJAX is widely used, this opens a significant gap in the Cross-Site Scripting protections that Content Security Policy offers.

Proposed resolution

- Add a new Drupal\Core\Ajax\CommandInterface() implementation that serializes an array of assets the calling page needs to add.
- Update \Drupal\Core\Ajax\AjaxResponseAttachmentsProcessor::buildAttachmentsCommands() to use the new command instead of rendering the assets to HTML and using an append/prepend command.
- Update Drupal.AjaxCommands with a new method to execute the corresponding command and add assets in a CSP-safe manner (see https://github.com/jquery/jquery/issues/3969).
- Deprecate \Drupal\Core\Ajax\AddCssCommand, which expects a rendered HTML string.

Remaining tasks

- Determine whether retaining support for to-be-deprecated 'browsers' is necessary (@see #3095113: Deprecate IE conditional comments support)

User interface changes

None

API changes

Release notes snippet

Comments

gapple created an issue. See original summary.

gapple’s picture

gapple’s picture

I'm uncertain if the distinction between header and footer JS assets is necessary in AJAX responses. My assumption is that all JS from the original page is already parsed before any AJAX requests are sent, so only the relative order of the new files matters, and appending them all to the end of the body tag would have the same affect as placing the header scripts in the page head.
No page scripts can be dependent on the new files required from an AJAX request (otherwise they would have already been include on the initial page), so it shouldn't be breaking to move them to the footer.

gapple’s picture

Status: Active » Needs review
StatusFileSize
new5.33 KB

Here's a quick patch to test the concept. One current limitation is that asset attributes (e.g. media for stylesheets) are not included in the data sent in the response.

I tested this by editing the "Featured Articles" view of the Umami profile. Before the patch, adding views.module.css (stable), node.css (classy), and picturefill.min.js are all blocked by Content Security Policy when loading the preview.
With the patch, the corresponding HTML elements are added to the page (verifiable in the inspector), the network requests are not blocked, and the picturefill library is available in the browser console.

Status: Needs review » Needs work

The last submitted patch, 4: drupal-3100151-4.patch, failed testing. View results

gapple’s picture

Status: Needs work » Needs review
StatusFileSize
new8.69 KB
new6.32 KB

This patch adds deprecation notices, any additional attributes set for an asset file (e.g. css media, js async), and the cache-buster query parameter when needed.

I set the deprecation notice to 9.0.0, as my understanding is that no new deprecations can be added in 8.9, and I'm not sure if this can be added in a 8.8 patch release?

Status: Needs review » Needs work

The last submitted patch, 6: drupal-3100151-6.patch, failed testing. View results

gapple’s picture

Saw some "CKEDITOR is not defined" errors while testing this:

https://www.html5rocks.com/en/tutorials/speed/script-loading/

Scripts that are dynamically created and added to the document are async by default, they don’t block rendering and execute as soon as they download, meaning they could come out in the wrong order. However, we can explicitly mark them as not async:

[
  '//other-domain.com/1.js',
  '2.js'
].forEach(function(src) {
  var script = document.createElement('script');
  script.src = src;
  script.async = false;
  document.head.appendChild(script);
});
gapple’s picture

Status: Needs work » Needs review
StatusFileSize
new8.9 KB
new1.58 KB
gapple’s picture

There's a TODO in the patch regarding support for the browser attribute of library assets. The conditional comments that the feature makes use of are unsupported in IE10+, and Drupal now only supports IE11.
#3095113: Deprecate IE conditional comments support

If an asset has any browser conditions set, it could just be omitted from the AJAX response's assets.

Status: Needs review » Needs work

The last submitted patch, 9: drupal-3100151-9.patch, failed testing. View results

gapple’s picture

I ran one of the failing tests locally and it passed, so I'm not sure if this change introduces a timing issue since it's not blocking rendering in the same way as before.

gapple’s picture

Issue summary: View changes

Removal of 'browsers' was postponed to 10.0 (with deprecation in 9.1).

gapple’s picture

\Drupal\Core\Ajax\AddCssCommand was introduced for IE <= 9, because they didn't support @import statements if they were in a PrependCommand.

Drupal 8 no longer uses @import statements (#2897408: Remove IE9 support from CssCollectionRenderer and provide it in contrib instead), and has now also removed the IE9 support code from AddCssCommand (#3100147: Remove @import parsing from Drupal.AjaxCommands.prototype.add_css), so AddCssCommand could be deprecated (in 9.1), and removed (in 10.0) separately from this issue.

gapple’s picture

Version: 8.8.x-dev » 8.9.x-dev
gapple’s picture

Issue tags: +Security improvements

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

gapple’s picture

gapple’s picture

Content Security Policy added a submodule to override core to use this approach #3166840: Override core's handling of library assets in AJAX responses

Some refactoring was done that should be ported to this patch.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

gapple’s picture