Is it worth it to include the contrib minor version? It's additional detail than previously, where the Compatibility-Major.PatchLevel
scheme melded together changes that would now be separated to semantic minor and patch releases. I expect in most cases update functions are going to be tied to minor-level changes, so there would be a lot of M0101
, M0201
, M0301
....
The minimum schema version sets the lower bound of numbering at 8000 while it still exists, but the default recommendation could just be Mdddd
(major without leading zero + 4 digit increment). Only a note would be required for pre-stable 0.x.x versions to start the update numbering at 8001.
Contrib modules could choose to include the minor version if they will be concurrently supporting minor releases for a longer duration, or just leave an arbitrary gap (e.g. +10) to account for the potential of patch-level updates.
Applying default-src 'none'
to other private files should only have an impact if sites were serving HTML files, and would be irrelevant to other responses like image files, so it might be worth leaving for discussion in
📌
Add a default CSP and clickjacking defence and minimal API for CSP to core
Active
or breaking out to a separate issue. It could then be done in combination to applying the same change for the files directory htaccess so that private and public files would have consistent behaviour, like SVGs would with this issue.
@timohuisman that's odd, but I guess not unexpected that a response might not have a content type header.
I think I would prefer to update the patch so that it only excludes content types and response classes that it knows are safe.
I don't think a short persistent login lifetime is useful, since PL cookies are intended to have a longer lifetime than session cookies, and are ineffective in the inverse case.
If you can better outline your use-case, and how it's addressed by allowing short PL lifetimes, please reopen the issue.
Quick fix to add new words to cspell dictionary
Made a MR with changes from patch file.
@damienmckenna
Possibly - the full violation report should include an original-policy
property that you can verify if clients are receiving the updated directive.
Including a hash, nonce, or using 'strict-dynamic'
would also disable inline scripts, but it sounds like you're not using any of those.
('inline'
isn't a valid directive source, but it should just be ignored).
Updated the patch from #105 in a MR
- Move allowedSlots
property declaration to constructor; replaces mismatched default value on property (#106)
- Added missing early return in set()
(#113)
- Override setMultiple()
to only count()
extra items once before removal (#108)
- Code style fixes
Can you elaborate on your use-case of this short of a token lifetime, and why you would like to do this with Persistent Login rather than setting a short session lifetime or use a module that enforces a session timeout after a configurable period?
With the module's recommended setting of the cookie_lifetime
to 0
, a browser will keep the session cookie until the browser is closed (provided it isn't still kept longer because of a session restore feature). With browsers remaining open in the background even when all windows are closed, the short-lifetime PL token could expire before the browser cleans up the session cookie.
Since the session cookie takes precedence, it will in most cases likely outlive a short persistent login.
The test includes a couple keys previously supported by core, but not an unknown key (e.g. the csp example in the issue summary)
Something that came up in #2868079: Add a default Content-Security-Policy-header for svg files → is that there may be a need for different policies in certain cases - uploaded files should probably have scripts and styles blocked, though SVGs may need inline styles, image data, and fonts. Private files can have the header applied from the event subscriber, but public files would require an addition to .htaccess.
Instead of a single set of policies, the service parameter could be keyed by use:
default:
report_only:
enforced: "script-src * 'unsafe-inline'; object-src 'none'; frame-ancestors 'self'"
private_files:
report_only:
enforced: "default-src 'none'"
svg:
report_only:
enforced: "default-src 'none'; style-src 'unsafe-inline'; image-src data: font-src: data:"
👋 CSP module maintainer here.
CSP module provides
APIs for modules to amend a site's policy →
. In most cases it's pretty straightforward, but gets more involved for cases where it's not just adding an additional external domain to a directive. The goal of the module is that anyone can install the module for a security improvement, that it's easily configurable for site builders to add exceptions not captured automatically (& ideally harden the default config with basic knowledge), but also flexible for developers to handle any more complex uses (and to do so safely without breaking other site functionality).
Module subscribers with the same priority will execute after core, so it's the module's responsibility to preserve/amend/replace any value previously set by core.
CSP has its own issue for applying a different policy to private files (
✨
Provide different CSP policy for private files
Active
), though there hasn't been any work done on it so far. If a special value is added to core for SVGs, then I'll update the module to match. If there's consensus on what should be added to core, I'll probably have a new release of the module before it's committed to core 🙂.
Alternately, the CSP module would be a good way to evaluate a potential solution (at least for private files, I'm not sure about the module altering htaccess), with users who have opted in to using a policy on their site. It would have to be an optional feature for the current major version, but could be enabled by default for new modules installs (and a site status message could prompt existing sites to try enabling it).
This issue for core would add a static policy that can be configured in a service parameter:
📌
Add a default CSP and clickjacking defence and minimal API for CSP to core
Active
Instead of providing a single policy set, maybe it should be keyed policies to allow for additional cases (e.g. default, private-files, svg, etc, each with report-only and enforced values).
Better description of alter priority; example of nonce & fallback
Opened 🐛 ConfigFormBase::formatMultipleViolationsMessage() return type change will cause error Active as a bug from this change.
gapple → created an issue.
Ran into an issue with the the return type change, since I was developing a module using the latest version of core and copied the new return type hint.
When using the new return type in an override with a stable version of core <=11.0.5, the child class is returning a wider type than it's parent.
When using the old return type in an override with 11.x-dev, parent::formatMultipleViolationsMessage()
returns a wider type than the child class.
For a module to maintain compatibility with older versions of core and not break if it calls the parent method which now returns Markup
, it will need to change the value returned from its parent:
$parent = parent::formatMultipleViolationsMessage($form_element_name, $violations);
if ($parent instanceof TranslatableMarkup) {
return $parent;
}
else {
// phpcs:ignore Drupal.Semantics.FunctionT.NotLiteralString
return $this->t((string) $parent);
}
Once the module drops support for older versions of core, it can update its typehint and remove the extra code.
\Drupal\update\UpdateSettingsForm
also needs its return type updated.
The current MR would address multiple violations on a non-sequence value, but if an item within a sequence has multiple violations it looks like only the last one will be kept.
ConfigFormBase::formatMultipleViolationsMessage()
also assumes it's being called on a sequence, so multiple constraint violations on a non-sequence value would still be formatted like "Entry 0: Constraint 1 message \n Entry 1: Constraint 2 message".
Forms can override the method to change the output based on the form element name in this case though.
Found a couple more incorrect references
Changes to provide a form error if an unavailable reporting plugin is referenced in config are in the 2.1.1 release.
Be sure to run module updates, and let me know if you continue to see a similar error.
Is this after updating from 1.x to 2.x? Do you have a pending site update?
https://www.drupal.org/docs/extending-drupal/contributed-modules/contrib... →
The sitelog reporting handler was removed from 2.x in favour of the
Reporting API module →
, and an update will disable policy violation reporting on a directive if it has not been changed to a supported alternative.
It is still possible to better handle the configuration referencing an unavailable reporting plugin there though...
🤞 that's a final fix for this issue.
Just wanted to also note that the tokens in the database aren't accepted even if provided by a browser, and this cleanup is only really needed for sites with tokens configured to a permanent lifetime, so there isn't a problem if the update is not completed on a site (provided it doesn't block any other necessary updates).
That's odd... On the last iteration $sandbox['#finished'] = $sandbox['processed'] / $sandbox['total'];
should equal 1
to complete the batch.
Are there tokens stored for the anonymous user (uid:0)? That would increase $sandbox['total']
to one more than will be returned in the $token_users
queries - but that shouldn't happen so some checks might need to be added elsewhere 😬.
Didn't catch @jabeler's review before - updating to RTBC :)
Bumping priority - CSP 1.x is still the most used version at this time, but is no longer receiving feature updates.
This is fixed in 8.x-1.9 and 2.2.3
The double-hashing was fixed in 8.x-1.7 and 2.2.1
Cleaning up tokens for deleted users was fixed in 8.x-1.9 and 2.2.3
Thanks for the quick patch & review :)
New patch releases should be available shortly.
@sokru My understanding is that whole page caching of a response with a nonce can be acceptable: https://serverfault.com/a/1064775/66144
Using a render placeholder ensures that a new nonce is sent for any request from an authenticated user or otherwise not served from the page cache, where new markup could be placed on the page but could only be aware of an outdated nonce value.
An issue with the proxy / edge cache worker example is that that using a predictable placeholder like NGINX_CSP_NONCE
could allow any injected content to use that value as well and have it replaced with the valid nonce.
The current patch also has this issue, as sprintf('nonce_%s', Crypt::hashBase64(__METHOD__))
will result in a known static value. The CSP module uses a random value in its placeholder so that it's not predictable between requests, but using the site's hash_salt would would also work as a way to provide a consistent but unique and unpredictable placeholder value for a site.
@rgpublic A hash can't be used on the external gtm
or gtag
js because their contents aren't known or static. The loader file could change at any time, making the hash invalid, and any additional dynamically loaded scripts won't be permitted.
The tag manager script loader will propagate the nonce to additional scripts (as verified by @chrissnyder in #12), without requiring the originating page to know the script contents (for a hash) or source domain (for non-tag manager scripts) to place in the policy.
@berliner From the one post I've seen that's worked through the impact of full-page caching with a CSP nonce https://serverfault.com/a/1064775/66144:
Let's say you as an attacker are served the same nonce as your victim on a response to the URL /foo/bar. Now you could use this nonce in your XSS payload. Let's consider two of the most common XSS scenario's:
- URL parameter based reflected XSS: /foo/bar?payload=... will likely have a different cache key than /foo/bar, therefore causing the user to be delivered a fresh response with a new nonce. The attack fails.
- Stored XSS in dynamic HTML: your payload is now included when the user loads /foo/bar. However, if the user refreshes and gets a response from the cache, then this won't include your injected XSS payload. Alternatively, if they get a fresh response they will also get a fresh nonce. In either case the attack fails.Now, this doesn't mean that this situation can never be exploited: for example, if /foo/bar has vulnerable JavaScript that modifies the DOM in a way that the attacker payload can be injected it might possible to use the cached nonce. Think of DOM based XSS attacks via data in the # segment of a URL or in AJAX responses.
It will be much trickier for an attacker to exploit the latter situation though, which may be sufficient since the point of CSP is to just provide defense-in-depth; it can not guarantee immunity for 100% of XSS attacks.
For Drupal, that means to not use AJAX responses to inject unsanitized markup, from user provided data, containing an inline script with a nonce attribute. This would also only be an effective attack against anonymous users on a single page for the duration of the calling page's cache lifetime, and other pages that receive the injected inline script with a nonce (and the original page when it's re-rendered with a new nonce) will send a violation report that should prompt investigation to address the underlying markup injection.
(AddJsCommand
for adding new script files to the page through AJAX does not (currently) propagate the page's nonce to the new scripts, so they must be permitted by the originating page's policy through another source like 'self'
.)
The CSP module includes a test that authenticated users receive a unique nonce for each request of a page that is cacheable in the dynamic page cache.
----
I've seen an approach where an edge cache worker handles replacing a placeholder value from the application, so that even responses served from the cache receive a unique nonce. But if the worker fails then responses are receiving a truly static value for all requests, and some examples I've seen have used a potentially predictable placeholder value such that markup injection would also be replaced with a valid nonce.
This would be possible with the CSP module by swapping out the Nonce service to one that responds with a static nonce value that includes some secret shared between the application and edge cache.
Removed "Define accepted keys" option from summary, update how CSP now uses #attached
.
Slightly different title; "Don't prevent modules from using #attached for additional data" would mean the same thing.
- I'm not sure if this could be done entirely in csp.schema.yml by directives extending/overriding the basic constraints in the csp_directive_source_list
schema, or would need a custom validator.
- Would a custom validator receive context of the parent config item, in order to be able to filter the list of valid flags?
There's some additional validation constraints that are useful:
-
The reporting plugin id →
- Required / optional mapping keys
Some possible future enhancements:
- Only permit the 'flags' property on relevant directives
- Validate the 'flags' values permitted for the respective directive
- Validate directives sequence keys
Being fixed in one merge request on parent issue.
Being fixed in one merge request on parent issue
Rebased.
CSP is now working around core by decorating the core attachments processor service, but it would be nice to not have to do this, since it also requires any module implementing CSP's new options to do a moduleExists()
check.
- Squashed and rebased
-
Draft Change Record created →
-
Postponed issue for 12.x created
📌
[12.x] Set default Content-Security-Policy in services.yml
Postponed
I'm not sure why tests are failing on ImageStylesPathAndUrlTest:L315
Remaining questions:
- Service parameter name: http.response.content_security_policy (like http.response.debug_cacheability_headers) or csp.config (like cors.config) or ?
- Should any X-Frame-Options header present on a response be removed, since browsers will ignore it when CSP is present?
- Review additional directives to add by default: script-src * 'unsafe-inline'; object-src 'none'
I'm not sure about best practices, but I do have thoughts :)
Ideally, other modules use CSP's APIs so that site builders, or anyone else, just needs to enable the module and the site's policy will be updated as needed for the module's functionality to work. CSP handles any adjustments for core, but shouldn't take on the responsibility for every other contrib module or hosting platform.
It's definitely a challenge that every site may need a pretty bespoke policy for its functionality (and the dev/support team's capacity for monitoring and managing its strictness).
A lot of cases for scripts and styles are handled transparently by parsing library info, but more complex cases or non script/style sources will currently need to implement an alter subscriber (e.g. Google Tag Manager / Google Analytics, which uses a loader script and can use a nonce, and also needs to modify img-src
and connect-src
[
Merge Request pending on google_tag
✨
Support Content Security Policy
Needs review
]; or Google Fonts, where a local CSS file needs an external domain included in font-src
).
The render attached info should enable supporting some cases in a more direct way than an alter subscriber, and some more cases for libraries would be supported if/when
✨
Enable specifying additional directives in library definitions
Active
is implemented.
If someone's implementing a custom module instead of a public contrib module, then I'm not particularly worried. I assume by them implementing a CSP policy they'll know to make necessary policy modifications, and they can use the contrib module's subscriber as a reference.
There's a possibility of another contrib module that extends CSP's config form with a new tab that has checkboxes to enable policy modifications for additional services that are used without a module, but that could get fairly long and messy for a lot of services that any particular site is not going to use.
There was a GitHub repo some time ago that documented the directives required for a lot of different services - I'll have to search if it still exists and is up to date. If there isn't a good reference somewhere else, a page could be added to the module's documentation for services commonly used on Drupal sites, and then site builders could choose to apply the changes in their configuration or a custom subscriber according to their preference (and anyone can contribute new services or changes to the documentation).
----
Where the policy values are static and global (and the config form could be disabled so it's not used), adding them using config overrides in settings.php would also be an option, and would make it clearer that the base value is enabled too (so, e.g., img-src data:
isn't enabled without 'self'
when uncommented).
An option could be for libraries to specify if they should be a conditional include, so that their sources are only added when the library is present on the page instead of the current behaviour of always being added on every request. This would be one method to resolve the issue where a module may have optional libraries that are only included when a certain feature is enabled (e.g. IIRC webform has some libraries with external domains that are not used by default).
The webrtc
directive is defined in the CSP spec, but is not yet implemented by all browsers. The message is only displayed in the browser console, and it does not send a violation report if you have configured a reporting option.
https://www.w3.org/TR/CSP3/#directive-webrtc
The module config form defaults to 'block'
when enabled, but if the directive is not yet supported by a browser (or not present in the policy), then webrtc connections are allowed by the browser by default (webrtc 'allow'
is explicit, but functionally the same as omitting the directive). Nothing you can do about that, but if you're not using webrtc and want browsers to block it when they add support, you can enable the directive with 'block'
now.
@defcon0 the theme hook is a relatively new addition specifically for themes, since they can't register a subscriber for the long-available Policy Alter event for modules.
I've done documentation updates alongside the 2.x releases to enumerate the options for dynamically altering the policy:
https://www.drupal.org/docs/extending-drupal/contributed-modules/contrib... →
I personally learn best from functional examples, and the CSP module provides multiple examples by using the alter event itself.
Changing status from "needs work" to "no known problems, since 2.0.0 is released.
Thanks for testing out the new feature :)
That looks right. IIRC, hook_page_attachments
is called after the page contents are rendered and all the metadata is bubbled up. html_head is then rendered separately by the attachments processor without bubbling.
----
Won't be an issue until something else uses csp_nonce
with a fallback that's not 'unsafe-inline'
, but the fallback value should be an array so it can be merged.
$attachments['#attached']['csp_nonce']['script'][] = Csp::POLICY_UNSAFE_INLINE;
From the 17.20 InnoDB memcached Plugin documentation page:
The InnoDB memcached plugin was removed in MySQL 8.3.0, and was deprecated in MySQL 8.0.22.
*sigh*
One day core will run its tests with script-src 'self'; style-src 'self'
...
If CSP is going to have to replace that JS file in the library with its own version that applies a nonce, then it can also add a dependency on drupalSettings.
Attaching policy to render elements is now available in a 2.1.0-beta1 release, and provides a mechanism to attach a nonce with a placeholder lazy builder so that the element can be cached and still receive a new nonce value on each request.
https://www.drupal.org/docs/extending-drupal/contributed-modules/contrib... →
D11 is released, so I think this is fully resolved 🙂
This can be added to 2.2.0 or later
I prefer the not-needle version 🙂, but the book and shield alone don't seem very representative or informative of the module's purpose.
The most notable feature of the module is preventing Cross Site Scripting vulnerabilities, but I've lately been thinking of it more broadly as a form of firewall which filters allowable content (external assets / requests) for a site.
Enumable things
- Directives (default-src, script-src)
- Directive Schemas (source-list, ancestor-source-list, token-list)
- Keywords ('self', 'none')
Function type hints may cause some problems, where they're currently typehinted to string
, and would need to change to string|enum
.
2.x could accept both where possible, and translate to strings where necessary. 3.x can widen type hints to string|enum
, 4.x could narrow them to enum
where appropriate.
✨ Allow CSP to be added by render elements Needs review has been merged and will appear in CSP 2.1.0
This needs some work to function with the changes in that issue, and could be added in 2.2.0. (I'm hoping for some community feedback on the attached functionality before building more on top of it).
Change Notice and Docs update are complete
https://www.drupal.org/node/3468616 →
https://www.drupal.org/docs/extending-drupal/contributed-modules/contrib... →
Note that element directives may not appear in policy if not enabled in config
Feature has been merged; Changing status to Needs Work for writing a Change Notice and updating documentation.
I'll probably do a 2.1.0-beta1 release with some other changes after those are complete.
Minimum version has been bumped to 10.1, in order to use the TrustedCallback
attribute.
Opened some additional issues for other changes that were in the original merge request, to be added to future releases
🌱
Use Autowire & Autoconfigure for services
Active
🌱
Use Enums
Active
Your comment is correct.
Just make sure that your additional JS elements aren't being cached in the dynamic page cache if they're being added by something more granular then hook_preprocess_page()
, so that they receive the new nonce value each time.
2.1 should make this easier when it's released, by attaching information to the relevant render element and providing a placeholder for the nonce value ✨ Allow CSP to be added by render elements Needs review