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:
- A path-pattern that matches the
endpoint
value specified in the original host'sreport-to
section. - A
methods
attribute with the valuePOST
. - A
plugin
attribute with the valuerwserve-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'smessage-type
subsection must have thepolicy
entry turnedon
. - A subset of policy report data can be saved by configuring which fields to log.