My site is on Apache, the host has enabled 'mod_unique_id'
My CSP runs in the root .htaccess.
My host has given me 2 lines of code to put in the CSP to make an unrecognisable base64 NONCE code each time it's needed - particularly for PayPal.
What they sent (in bold):
`

<IfModule mod_headers.c>
    **Header set X-Nonce "expr=%{base64:%{reqenv:UNIQUE_ID}}"
    Header set Content-Security-Policy "expr=default-src 'self'; script-src 'self' 'nonce-%{base64:%{reqenv:UNIQUE_ID}}'"**`

The 2 lines of code go in my .htaccess somewhere, I'm pretty confident about the script-src but the bit that's throwing me is the expr=default-src: - is that a new directive?

This is the Header set Content-Security-Policy "frame-ancestors 'self' twitter.com t.co;block-all-mixed-content;default-src 'unsafe-inline' website).com https://www.paypal.com https://www.clarity.ms https://www.google.com 'nonce-YSBmcmllbmQgaXMgYSBwZXJzb29uIHRoYXQgd2Fsa3MgaW4gd2hlbiB0aGUgb3RoZXJzIHdhbGsgb3V0'

I need someone who knows about CSP, the directives and the workings of the NONCE to help me set it up. My regular developer can't help me, my host has no idea, nor can a reputable developer whom I call on.
Anyone? Thanks in anticipation, Steve

Dani AI

Generated

Short answer and what “expr=” means: that prefix isn’t a new CSP token — it’s Apache’s mod_headers/ap_expr mode telling Apache “evaluate this expression and use the result as the header value.” The CSP directives like default-src and script-src are normal CSP tokens that follow whatever value the expression produces. That’s why ’s read of the line was essentially correct: expr= makes Apache compute the header string rather than sending a plain literal. mod_headers docs · ap_expr reference. (httpd.apache.org)

About UNIQUE_ID and security: mod_unique_id does expose a per-request UNIQUE_ID, but it’s constructed from timestamp/pid/IP/counter — not a CSPRNG — so it’s not the recommended source for CSP nonces. The CSP spec and MDN both say nonces must be fresh, unpredictable, and should come from a cryptographically secure RNG (≈128 bits or more) for security-sensitive pages (payments, auth flows). If this is for PayPal-related flows, prefer an application-generated CSPRNG nonce. mod_unique_id docs · CSP nonce guidance (W3C/MDN) · MDN nonces overview. (httpd.apache.org)

Recommended, practical approach: generate a CSPRNG nonce in your app (so you can put the same value in the header and in the HTML). Example PHP pattern (16 bytes = 128 bits):

<?php
$nonce = base64_encode(random_bytes(16));          // cryptographically secure
header("Content-Security-Policy: script-src 'self' 'nonce-{$nonce}'");
?>
<script nonce="<?php echo htmlspecialchars($nonce, ENT_QUOTES); ?>">
  /* inline script allowed by this nonce */
</script>

This guarantees unpredictability and matching values. See PHP random_bytes() and CSP nonce docs. ()

If application-level generation is impossible, the Apache-only fallback (what sketched) is to use mod_headers/ap_expr to base64-encode reqenv(UNIQUE_ID) into the header — that requires Apache 2.4.10+ and the base64 ap_expr function — but treat it as a weaker fallback because of entropy concerns. Always verify with curl/DevTools that the header contains a single‑quoted nonce-... token and that the same raw base64 value appears as the nonce attribute on your script tag; mismatch or invalid characters will cause the browser to block the script. [ap_expr functions / base64] · [mod_headers notes]. (httpd.apache.org)

Recommended Answers

All 3 Replies

I am having some health issues and extreme brain fog, so while I do have experience with CSP, please bear with me.

My first question to you would be, what are you trying to accomplish by using CSP nonces? In theory, I completely get it that it allows you to whitelist specific inline script tags (and block all others that don't have a nonce), but does the content and demographic of your website call for such scrutiny over security?

Here at DaniWeb, the CSP we use is simply:

Content-Security-Policy: frame-ancestors 'none'; form-action 'self' https://www.paypal.com

Let's assume, however, that you do want to use CSP nonces. As far as expr=default-src, I think basically what that is doing is expr= sets the value of Content-Security_policy to everything following (e.g. starting with default-src 'self' ...).

would I have to alter where it says {SERVER-GENERATED-NONCE} to match with the CSP mod_unique_id

Yes, you would. I haven't used Apache in over a decade, but a Google search says you can do something such as (provided you have PHP short tags enabled):

<script nonce='<?= $_SERVER['UNIQUE_ID'] ?>'>

If your server doesn't have short tags enabled, you would do:

<script nonce='<?php echo $_SERVER['UNIQUE_ID']; ?>'>

Not my field of expertise but what I could gather from Google -

  1. The Header set X-Nonce line generates a base64-encoded nonce based on your value of the UNIQUE_ID environment variable provided by 'mod_unique_id'. This nonce is set in the response header named "X-Nonce."

  2. The Header set Content-Security-Policy line sets your CSP. It allows scripts only from the same origin ('self'), and for inline scripts, it uses a nonce generated based on your UNIQUE_ID.

The 'default-src' is a directive in the CSP that defines the default behavior for other resource types not explicitly specified. In the code you provided, 'default-src' is used to define the default policy for various resource types, but it's not clear from the context what the specific policy is.

If you want to specify additional policies for other resource types (images, styles, fonts), you can add them along with the 'default-src' -
Header set Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-%{base64:%{reqenv:UNIQUE_ID}}'; img-src 'self'"

The '{SERVER-GENERATED-NONCE}' in your HTML code, you have to replace the tag code with the actual nonce value generated by your server. Since you're using the 'mod_unique_id' approach to generate nonces, it should match the nonce generated in your CSP configuration, which will look like this -

<script nonce="{NONCE_VALUE_FROM_CSP}">
  (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
  new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
  j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
  'https://www.googletagmanager.com/gtm.js?id='+i+dl;var n=d.querySelector('[nonce]');
  n&&j.setAttribute('nonce',n.nonce||n.getAttribute('nonce'));f.parentNode.insertBefore(j,f);
  })(window,document,'script','dataLayer','GTM-WL8PRCS');
</script>

Replace {NONCE_VALUE_FROM_CSP} with the actual nonce value generated by your 'mod_unique_id' based CSP.

commented: Replace {NONCE_VALUE_FROM_CSP} with the actual nonce value generated by your 'mod_unique_id' based CSP. - where can I find it? +1
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.