Problem/Motivation

In order to safely add a nonce or hash to a directive, modules need to:

  • copy sources from any fallback directives
  • For hashes, initialize the other subdirective so that a hash value from the base directive doesn't apply (either through the other directive not existing on the final policy, or the value from the base directive later being copied)
  • check whether another module has already added 'unsafe-inline' but not another nonce or hash source
    • if 'unsafe-inline' is not present or is disabled (by an existing nonce or hash; or 'strict-dynamic'): append the nonce or hash
    • if 'unsafe-inline' is required by another module: authorize the necessary sources in another way - it's presumed 'unsafe-inline' is already present, but other modules (e.g. google_tag) may need to add additional external domains for dynamically loaded scripts
  • Modify the base directive with the same value for older browsers that don't support *-elem and *-attr

#3398536: Enable conditional/alternate directive values could either supersede this, or be used as the underlying method for implementing the source fallback functionality.

Proposed resolution

Create a PolicyHelper service with methods for altering a Csp object

  • appendNonce(Csp $policy, string $directive, $fallback = null, string $value = null) : bool
    If $value is not provided, retrieve the nonce from the csp.nonce service.
    Add a nonce to {$directive}-src-elem if possible (and the corresponding base directive. It is not necessary to alter the -attr directive since nonces can't be applied to attributes).
    If the nonce is not appended, add $fallback to the directives instead.
  • appendHash(Csp $policy, string $directive, string $type = 'elem', $fallback = null, string $value): bool
    Initialize the base directive and other subdirective.
    Add the provided hash to {$directive}-src-{$type} and {$directive}-src if possible.
    If the hash is not added, add the $fallback value.
  • requireUnsafeInline(Csp $policy, string $directive, string $type): void
    Ensure that the other subdirective does not fall back to the base directive.
    Require 'unsafe-inline' for a directive (and its base directive) by ensuring that any hashes or nonces are removed.
    Note: if this method is required, it should be called early in the policy alter event so that other modules can correctly add any needed fallback values.

Remaining tasks

User interface changes

n/a

API changes

New service

Data model changes

n/a

Issue fork csp-3410651

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

gapple created an issue. See original summary.

gapple’s picture

gapple’s picture

Issue summary: View changes

gapple’s picture

Now also included is PolicyHelper::requireUnsafeInline(), which will apply to the necessary subdirective and fallback, but ensure the other subdirective doesn't fall back (if it does not yet have a value).

It will remove any nonce or hash so that 'unsafe-inline' applies - this could cause problems if called after another module adds a nonce or hash that would require a non-'unsafe-inline' fallback, so it should be called in an alter subscriber that's prioritized to execute earlier than others.

gapple’s picture

Status: Active » Needs review
gapple’s picture

Issue summary: View changes
gapple’s picture

Issue summary: View changes
gapple’s picture

Issue summary: View changes
gapple’s picture

I altered the parameter order so that a fallback value is always required, and added a note on the method docblock about properly calling requireUnsafeInline() early.

gapple’s picture

Issue summary: View changes
gapple’s picture

Noticed some extra cases to test:
- if appending a new nonce or hash and the directive already includes a nonce, hash, or strict-dynamic (which disable 'unsafe-inline'), then the value should be appended (and not the fallback).
- if requiring 'unsafe-inline' and the directive includes 'unsafe-hashes'

Some cleanup is possible with 'unsafe-hashes':
- if 'unsafe-hashes' isn't present on an attribute directive (e.g. it was copied from the base directive), any hashes can be removed since they're not effective (assume they correspond to permitted hashes of elements).
- remove 'unsafe-hashes' from an element directive.

  • gapple committed 18c3135f on 8.x-1.x
    Issue #3410651: Add helper for adding nonce and hash sources
    

  • gapple committed fa42f1e6 on 2.x
    Issue #3410651: Add helper for adding nonce and hash sources
    
gapple’s picture

Status: Needs review » Fixed

  • gapple committed f7e0a12b on 8.x-1.x
    Issue #3410651: Add missing service definition for policy helper
    

  • gapple committed 8b546894 on 2.x
    Issue #3410651: Add missing service definition for policy helper
    

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.