0

I'm sending a CSP-report via report-to directive:

app.use(express.json({
    type: [
        "application/json",
        "application/csp-report",
        "application/reports+json"
    ]
}));

const _reportCSP = {
    endpoints: [
        {
            url: "https://myapp.com/reportCSP"
        }
    ],
    group: "csp-endpoint",
    include_subdomains: true,
    max_age: 31536000
};

res.setHeader("content-security-policy", `default-src 'none'; script-src https://*.someHost.com 'self' 'unsafe-eval' 'unsafe-inline'; style-src https://*.someHost.com 'self'; font-src https://*.someHost.com 'self'; img-src 'self'; connect-src https://*.someHost.com https://*.dropboxapi.co 'self'; frame-ancestors 'none'; report-to ${reportCSP.group};`);

res.setHeader("report-to", JSON.stringify(reportCSP));

An URL https://*.dropboxapi.co is used for the CSP-reports testing purposes only.

Based on this sample, I handle a CSP-report request in the following manner:

function echoReports(request, response) {

    for (const report of request.body) {
        console.log(`Report content: ${report}`);
        console.log(`Report content: ${JSON.stringify(report)}`);
    }
}

app.post("/reportCSP",

    async (req, res) => {

        console.log(`${req.body.length} CSP violation reports:`);

        echoReports(req, res);
    }
);

As a result, I get on a server-side:

1 CSP violation reports:
Report content: [object Object]
Report content: "[object Object]"

In DevTools I see the correct error report:

Refused to connect to 'https://content.dropboxapi.com/…' because it violates the following Content Security Policy directive: "connect-src https://*.someHost.com https://*.dropboxapi.co 'self'"

Refused to connect to 'https://content.dropboxapi.com/…' because it violates the document's Content Security Policy.

JSON.stringify(request) inside of echoReports(req, res) leads to:

TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Socket'
    |     property 'parser' -> object with constructor 'HTTPParser'
    --- property 'socket' closes the circle
    at JSON.stringify (<anonymous>)
    at echoReports (file:///app/src/server/app.mjs:2949:33)

Since I'm on Express.js 4.17.1, I don't use body-parser but use express.json(…) instead.

I've tried several approaches to print a content of the CSP-report on the server-side but no success.

Of course, any anti-banner, security and privacy protection software had been deactivated.

P.S. I've elaborated the question to make it more clear what's the problem I'm trying to solve.

Mike
  • 14,010
  • 29
  • 101
  • 161
  • Don't you have a typo in the value of your `connect-src` directive? Shouldn't `https://*.dropboxapi.co` be `https://*.dropboxapi.com`? – jub0bs Sep 21 '21 at 09:28
  • @jub0bs, that's typo is to check the CSP `report-to`, once I'll figure out how to get the reports, I'll fix it. – Mike Sep 21 '21 at 09:30
  • `${policyViolationReport.group}` evaluates to `"policyViolationReport"`, not `"/policyViolationReport"`. Where is the leading slash supposed to come from in `report-to ${policyViolationReport.group}`? – jub0bs Sep 21 '21 at 09:36
  • I assume, that `${policyViolationReport.group}` is referencing to `group: "policyViolationReport"`, which means `${policyViolationReport.group}` = `"policyViolationReport"`. Should the group start with `/`? – Mike Sep 21 '21 at 09:39
  • 1
    I don't know what the group should be, but the value of the `report-to` directive should definitely not be `policyViolationReport`. Try to hardcode `/policyViolationReport` in the CSP header and see if that changes anything. – jub0bs Sep 21 '21 at 09:40
  • I'll check this setting. – Mike Sep 21 '21 at 09:41
  • Any luck with that change? – jub0bs Sep 21 '21 at 12:40
  • @jub0bs, I've completely elaborated the question, now it contains more precise info and follows the recommendations from Google's team but I still get `[object Object]` instead of normal content. – Mike Sep 25 '21 at 13:54

1 Answers1

3
  1. Check that the browser receives the Report-To HTTP header, the guide is here.

  2. Check network log using the chrome://net-export/ and https://netlog-viewer.appspot.com tools:

  • For latest Chrome versions open the chrome://net-export page on a separate tab and enable recording of network log to disk.
  • Reload the tab with the page which publishes the Report-To header.
  • Stop recording of network log to disk with the <Stop logging> button at the chrome://net-export page.
  • Open the https://netlog-viewer.appspot.com log viewer on a separate tab, and select the file of your log.
  • Select "Reports" in the left menu, and in the "Queued reports" section, click on the link "Show raw report". You'll see CSP reports created and queued or empty section if all reports have been sent.
    Also have a look at the bottom of the "Per-origin config" table: Per-origin config table In the "Uploads" column the number of sent reports will be indicated.

A complete step-by-step guide is here

  1. The page itself that publishes a Report-To header must be loaded with HTTPS:, otherwise reports will not be sent. Endpoint's Url should be HTTPS: too.

  2. Note if you site works behind Cloudflare, the Report-To header will not work property.

update

It seems that this code snippet:

app.post("/policyViolationReport",

    async (req) => {
        console.log("Bingo! CSP Report occurred.");
    });

will not receive violation reports because violation reports are not sended as an ordinary POST data (i.e. &param=value). Browser sends just body of JSON.

Try to use this:

app.use(
    bodyParser.json({
        type: ['application/json', 'application/csp-report', 'application/reports+json'],
    })
);
    
app.post('/policyViolationReport', (req, res) => {
    console.log(req.body);
    res.status(204).end();
});
Mike
  • 14,010
  • 29
  • 101
  • 161
granty
  • 7,234
  • 1
  • 14
  • 21
  • It looks like you are just processing CSP reports incorrectly on the server side. Pls see **updated** section of answer. – granty Sep 21 '21 at 17:31
  • Based on [this](https://developers.google.com/web/updates/2018/09/reportingapi) article, I've succeeded to figure out the way to implement the back-end side. The only issue, when I try to iterate over `req.body`, I get `report.body: undefined`. I need to find out why during the iteration over `req.body` I get `undefined`. – Mike Sep 21 '21 at 21:21
  • `request` is an array, so you have to iterate over this array, but not over `request.body` which is not present. Try `for (const report of request) { console.log(report.body); }` - the `report.body` will be defined for each element of array. – granty Sep 22 '21 at 00:45
  • I've tried to print `req.body.blockedURL` and `req.body` and in both cases I get `undefined`. Really can't find out why the CSP-request can't be parsed. – Mike Sep 24 '21 at 08:39
  • 1
    Check what do you really had get in the report: `console.table( JSON.stringify( request ) )`. Also `bodyParser()` provides access to [RAW data](https://stackoverflow.com/questions/38468896/how-to-use-bodyparser-raw-to-get-raw-body) of request, But you do not use external `body-parser` middleware, you do use Express build-in parser of body. I do not know how to access RAW data in this case. – granty Sep 24 '21 at 12:57
  • Well, it still doesn't work. `bodyParser()` doesn't show the RAW data. I'll wait until `report-to` will be implemented in Firefox, perhaps `report-to` is still under development. – Mike Sep 24 '21 at 15:49
  • 1
    [Here](https://csplite.com/csp/test229/) you can check your browser for support of `report-to` directive. The `report-to` is not under development in Chrome, it supports it already for 3 years or more. But Firefox/Safari still not implemeted it. – granty Sep 25 '21 at 08:06
  • I've elaborated the question with more code-samples using `JSON.stringify(…)` but still no success. I either get `[object Object]` or `"[object Object]"` even after `JSON.stringify(…)`. – Mike Sep 25 '21 at 14:04