You are here

function _seckit_csp_report in Security Kit 7

Same name and namespace in other branches
  1. 6 seckit.module \_seckit_csp_report()

Log CSP violation reports to watchdog.

1 string reference to '_seckit_csp_report'
seckit_menu in ./seckit.module
Implements hook_menu().

File

./seckit.module, line 339
Allows administrators to improve security of the website.

Code

function _seckit_csp_report() {

  // Only allow POST data with Content-Type application/csp-report
  // or application/json (the latter to support older user agents).
  // n.b. The CSP spec (1.0, 1.1) mandates this Content-Type header/value.
  // n.b. Content-Length is optional, so we don't check it.
  if (empty($_SERVER['CONTENT_TYPE']) || empty($_SERVER['REQUEST_METHOD'])) {
    return;
  }
  if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    return;
  }
  $pattern = '~^application/(csp-report|json)\\h*(;|$)~';
  if (!preg_match($pattern, $_SERVER['CONTENT_TYPE'])) {
    return;
  }
  $options = _seckit_get_options();

  // If SecKit is disabled, do not process the report.
  if ($options['seckit_advanced']['disable_seckit']) {
    return;
  }

  // If the CSP feature is currently disabled, do not process the report.
  // This could be considered inaccurate (violation reports for a cached page
  // which was generated with CSP headers might be expected to be processed);
  // but a single on/off switch for both aspects of CSP support seemed sanest,
  // and this approach ensures that we do not process requests sent to this URL
  // before CSP headers have ever been enabled (prior to which reporting limits
  // are usually ignored to facilitate initial CSP development).
  if (!$options['seckit_xss']['csp']['checkbox']) {
    return;
  }

  // Check for flooding.
  // Do not write to watchdog when our limits are exceeded.
  $enforce_limits = !$options['seckit_advanced']['unlimited_csp_reports'];
  if ($enforce_limits && _seckit_csp_report_flooding_detected()) {
    return;
  }

  // Read the report data.
  if ($enforce_limits) {
    $max_size = (int) $options['seckit_advanced']['csp_limits']['max_size'];
    $reports = file_get_contents('php://input', FALSE, NULL, 0, $max_size + 1);
    if (strlen($reports) > $max_size) {
      return;
    }
  }
  else {
    $reports = file_get_contents('php://input');
  }
  $reports = json_decode($reports);
  if (!is_object($reports)) {
    return;
  }

  // Log the report data to watchdog.
  foreach ($reports as $report) {
    if (!isset($report->{'violated-directive'})) {
      continue;
    }

    // Log the violation to watchdog.
    $info = array(
      '@directive' => $report->{'violated-directive'},
      '@blocked_uri' => $report->{'blocked-uri'},
      '@data' => print_r($report, TRUE),
    );
    watchdog('seckit', 'CSP: Directive @directive violated.<br /> Blocked URI: @blocked_uri.<br /> <pre>Data: @data</pre>', $info, WATCHDOG_WARNING);
  }
}