CSP violation report aggregation using Nginx only

Posted on 24 April 2016 in misc

There is a powerful feature of Content Security Policy called Reporting. Web application owner can specify special URI via report-uri directive to which the user agent will send reports about policy violation. In testing environment it helps to find missed resource inclusions so with enforced policy your web application will not work correctly. In production environment it helps to detect that someone or something trying to attack your site users with XSS attack or inject unwanted 3rd party JavaScript code. So it is a good practice to always include report-uri in CSP policy of your web application.

The violation reports are in JSON format and delivered by HTTP POST requests. Let's try to make CSP violation report aggregation using Nginx only.

For the first we need to initialize separate log format for logging POST request body. We also making auxiliary map because we will accept only requests with a Content-Type header field of application/csp-report or application/json.

http {
...
    log_format  csp_report '$remote_addr - [$time_local] '
        '"$http_referer" "$content_type" "$http_user_agent" "$request_body"';

    map $status $csp_loggable {
        "200"  1;
        default 0;
    }

    map $content_type $csp_bad_content_type {
        default 1;
        "application/json" 0;
        "application/csp-report" 0;
    }
...
}

To log POST request body we use variable $request_body. According to Nginx documentation:

The variable’s value is made available in locations processed by the proxy_pass, fastcgi_pass, uwsgi_pass, and scgi_pass directives.

So we need to make "fake" location and use it in proxy_pass directive.

server {
...
    location /fakeloc {
        return 200;    
    }
...
}

The main part of our Nginx based reporting is location /cspreport:

server {
...
  location /cspreport {
        if ($request_method != "POST") {
            return 405;
        }

        if ($csp_bad_content_type) {
            return 415;
        }

        access_log  /var/log/nginx/csp.log csp_report if=$csp_loggable;
        proxy_set_header Host $host;
        proxy_pass http://SERVER_IP:80/fakeloc;
    }
...
}

The advantages of this solution are:

  • There is no need in separate script for report aggregation
  • Built-in log rotation

Сons are:

  • Nignx config language is not so powerful as e.g. Python

For analysis of such logs can be used CSP Reporter.