Process and log incoming policy reports

Policy Reports

Preliminaries

How to designate your server as a policy report handler for incoming security and error reports.

A browser can be instructed to send policy reports back to the server when policy violations or network errors occur. The report-to header declares an endpoint where such reports are to be sent.

Policy reports are sent by the browser using an HTTP POST method with a content type of application/reports+json. A properly configured endpoint should accept the POST data, process it, and respond to the browser with status code 204 (No Content).

Any HTTP server capable of handling such reports can be used as a report-to endpoint. The built-in rwserve-policy-reports plugin can be used for this purpose, turning any ordinary RWSERVE web server into a policy report handler as well.

The rwserve-policy-reports plugin processes the incoming data and writes it to the system log using the systemd/journald logging facility. In this way, policy reports are interleaved with request-response logging entries. Like all RWSERVE log entries, they are identified with a unique prefix. In this case the prefix is 'policy'.

Because the browser sends policy reports out-of-band, and because they are often intentionally delayed by the browser, the request/response that triggered the report will not always be adjacent (in the logged messages) to the corresponding policy report that was generated.

The rwserve-policy-reports plugin logs the incoming data, but a separate process should periodically scan the log and take appropriate action. The Analytics CLI is capable of doing that. It can read and monitor 'policy' type messages, and it is useful to run this daily as a cron job.

Configuration

An endpoint should be configured to accept and process incoming policy reports. Do this by adding an entry to the router section, which is subordinate to the plugins section.

The entry should have three values:

  1. A path-pattern that matches the endpoint value specified in the original host's report-to section.
  2. A methods attribute with the value POST.
  3. A plugin attribute with the value rwserve-policy-reports.

In order for this to be effective, the logging section's message-type subsection must set policy to on.

Individual fields from the incoming report may be captured using standard logging configuration rules, such as *abbr, *format, *fg, and *bg.

The list of fields in the incoming policy report is not standardized, and because this is an experimental technology, you should expect new field names to be introduced, and existing field names to change. In order to capture and record all incoming data fields, a 'default' line item should be declared.

EBNF

SP ::= U+20
CR ::= U+0D
ASTERISK ::= U+2A
HYPHEN ::= U+2D
QUESTION-MARK ::= U+3F
SOLIDUS ::= U+2F
GRAVE-ACCENT ::= U+60
LEFT-CURLY-BRACKET ::= U+7B
RIGHT-CURLY-BRACKET ::= U+7D
file-system-chars ::= (ALPHA | DIGIT | )*
wildcards ::= ASTERISK | QUESTION-MARK
path-pattern ::= (SOLIDUS | file-system-chars | wildcards)*
delimited-path-pattern ::= GRAVE-ACCENT path-pattern GRAVE-ACCENT
method-name ::= 'HEAD' | 'GET' | 'PUT' | 'DELETE' | 'POST' | 'PATCH' | 'OPTIONS' | 'TRACE'
methods-attribute ::= ASTERISK 'methods' EQUALS-SIGN (method-name COMMA)*
plugin-name ::= (ALPHA | DIGIT | HYPHEN)*
plugin-attribute ::= ASTERISK 'plugin' EQUALS-SIGN plugin-name CR
router-entry ::= delimited-path-pattern SP methods-attribute SP plugin-attribute CR
router-section ::= 'router' SP LEFT-CURLY-BRACKET CR
router-entry*
RIGHT-CURLY-BRACKET CR

† Legal file system characters vary by platform

Cookbook

Example 1: reporting security violations to origin server
host {
hostname domain.tld
modules {
policies on
}
logging {
message-type {
policy on
}
}
policies {
content-security-policy {
default-src 'self'
report-to csp-endpoint
}
report-to {
csp-endpoint {
endpoint `https://domain.tld/csp-notifications`
max-age 2592000
}
}
}
plugins {
router {
`/csp-notifications` *methods=POST *plugin=rwserve-policy-reports
}
}
}
Example 2: reporting errors to endpoint on separate domain

The server to be monitored is configured with:

host {
hostname domain.tld
modules {
policies on
}
policies {
network-error-logging {
report-to nel-endpoint
max-age 2592000
}
report-to {
nel-endpoint {
endpoint `https://server-monitor.tld/nel-notifications`
max-age 2592000
}
}
}
}

The server to receive browser-generated error reports is configured with:

host {
hostname server-monitor.tld
modules {
cross-origin on
}
logging {
message-type {
policy on
}
}
request {
cross-origin {
`/nel-notifications` *origin="domain.tld" *headers=content-type *methods=POST
}
}
plugins {
router {
`/nel-notifications` *methods=POST *plugin=rwserve-policy-reports
}
}
}
Example 3: logging a subset of the policy report data
host {
hostname domain.tld
modules {
policies on
}
logging {
message-type {
policy on
}
policy {
type *abbr=TYP *format='40...40' *fg=red
age *abbr=AGE *format='40...40'
blocked-uri *abbr=B-URI *format='40...40'
disposition *abbr=DISP *format='40...40'
document-uri *abbr=D-URI *format='40...40'
effective-directive *abbr=E-DIR *format='40...40'
elapsed-time *abbr=ELAP *format='40...40'
line-number *abbr=LN *format='40...40'
method *abbr=ME *format='40...40'
phase *abbr=PH *format='40...40'
protocol *abbr=PROT *format='40...40'
referrer *abbr=RF *format='40...40'
sampling-fraction *abbr=S-FRAC *format='40...40'
server-ip *abbr=S-IP *format='40...40'
source-file *abbr=S-FIL *format='40...40'
status *abbr=ST *format='40...40'
status-code *abbr=ST *format='40...40'
original-policy *abbr=O-POL *format='40...40'
url *abbr=URL *format='40...40'
user-agent *abbr=UA *format='40...40'
violated-directive *abbr=V-DIR *format='40...40'
// default *abbr=none *format='40...40' // uncomment to capture other fields
}
}
policies {
report-to {
default {
endpoint `https://domain.tld/default-notifications`
max-age 2592000
}
}
}
plugins {
router {
`/default-notifications` *methods=POST *plugin=rwserve-policy-reports
}
}
}

Review

Key points to remember:

  • The rwserve-policy-reports plugin turns an RWSERVE web server into a policy report handler.
  • Incoming policy reports are captured and logged via the system log using journald.
  • The logging module's message-type subsection must have the policy entry turned on.
  • A subset of policy report data can be saved by configuring which fields to log.

Process and log incoming policy reports