DEV Community

Cover image for Configuring CSP nonce for nginx 1.28 on Ubuntu 24 LTS using NJS
Damian
Damian

Posted on

Configuring CSP nonce for nginx 1.28 on Ubuntu 24 LTS using NJS

Content Security Policy

CSP is a way to tell a browser that only the provided locations and/or resources should be loaded.

Learn more about CSP on OWASP.

Nonce

It is an expression of CSP's header fetch directive. Essentially, it's a way to tell the browser to only load styles and scripts that are having nonce HTML attribute's value matching whatever is present in the CSP's header.

Learn more about CSP's nonce expression syntax on MDN.

Example nonce configuration

Server responds with headers:
...
Content-Security-Policy: script-src 'self' 'nonce-foo';
...

HTML page:
...
<script nonce="foo">console.log('safe');</script> <!-- safe, foo === foo  -->
<script nonce="bar">console.log('unsafe');</script> <!-- unsafe, foo !== bar -->
<script>console.log('unsafe');</script> <!-- unsafe, no nonce value -->
...
Enter fullscreen mode Exit fullscreen mode

Nginx

Nginx is an opinionated HTTP web server, which we'll be using for educational purposes of this article.

NJS

NJS (Nginx JavaScript) is a dynamic module of nginx that allows its advanced configuration via JavaScript-like sources.

It exposes useful APIs, such as Crypto.

The process

1. Adding nginx official repo to apt sources

Please follow nginx's linux packages instructions.

2. Installing nginx-module-njs

Since we're now having nginx's repo in apt sources - let's install njs as dynamic nginx module:

sudo apt install nginx-module-njs
Enter fullscreen mode Exit fullscreen mode

3. Configuration of nginx to load njs module

We need to load the module in nginx.conf:

# /etc/nginx/nginx.conf
+ load_module modules/ngx_http_js_module.so;
+ load_module modules/ngx_stream_js_module.so;
http {
+  js_path "/etc/nginx/njs/";
...
Enter fullscreen mode Exit fullscreen mode

Also, it would be nice to either allow passing secret key for nonce generation via docker's container env variable, or set it explicitly:

# /etc/nginx/nginx.conf
+ env NONCE_SECRET_KEY;
load_module modules/ngx_http_js_module.so;
load_module modules/ngx_stream_js_module.so;
Enter fullscreen mode Exit fullscreen mode

4. Writing NJS script to generate nonce values

This is an example of NJS script that leverages nginx's request_id variable to create SHA1 HMAC hex string:

// /etc/nginx/njs/nonce.njs

import crypto from 'crypto';

function nonce(r) {
  const h = crypto.createHmac('sha1', process.env.NONCE_SECRET_KEY);
  h.update(r.variables.request_id);
  return h.digest("hex");
}

export default { nonce };
Enter fullscreen mode Exit fullscreen mode

5. Setting CSP header using NJS

Now, whenever we need to apply the CSP header:

# /etc/nginx/nginx.conf, /etc/nginx/sites-available/yoursite, ...
...
+ js_import main from nonce.njs;
+ js_set $csp_nonce main.nonce;
+ add_header Content-Security-Policy "script-src 'self' 'nonce-$csp_nonce'";
...
Enter fullscreen mode Exit fullscreen mode

Let's reload nginx configuration:

sudo service nginx reload
Enter fullscreen mode Exit fullscreen mode

At this point we should see the header being set correctly in the response.

6. Setting nonce in HTML pages (OPTIONAL)

Since we're now having the csp_nonce variable in place, produced by NJS, let's use it in nginx's sub_filter directive:

# /etc/nginx/nginx.conf, /etc/nginx/sites-available/yoursite, ...
...
+ sub_filter_once off;
+ sub_filter CSP_NONCE $csp_nonce;
...
Enter fullscreen mode Exit fullscreen mode

Let's reload nginx configuration:

sudo service nginx reload
Enter fullscreen mode Exit fullscreen mode

Now, in HTML page we can reference it like this:

<script nonce="CSP_NONCE">console.log('nonce works!');</script>
Enter fullscreen mode Exit fullscreen mode

Summary

We have successfully configured Content Security Policy header in nginx, using nginx JavaScript dynamic module.

Further enhancements and/or adjustments can be made to meet our particular needs.

Cover image

Cover image was generated using AI.

Top comments (0)