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 -->
...
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
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/";
...
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;
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 };
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'";
...
Let's reload nginx configuration:
sudo service nginx reload
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;
...
Let's reload nginx configuration:
sudo service nginx reload
Now, in HTML page we can reference it like this:
<script nonce="CSP_NONCE">console.log('nonce works!');</script>
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)