Capture Content Security Policy (CSP) Violations in PHP
Sending CSP Violations back to your server
When somebody violates your CSP rules, there is a great feature that can setup for supporting browsers to send back the violations to your server to be saved, processed or whatever. This is a great feature because you can stop a possibly malicious piece of code from executing and learn which scripts may have vulnerabilities in your code.
How are these CSP Violations sent back to your server
CSP violations are sent back as JSON string. The example below is taken from Mozilla’s site showing a violation in JSON with 5 parts to the violation. The “blocked-uri” is the resource that violated the policy. In our PHP script at end of this, I’ll show how to decode the JSON string.
{
"csp-report":
{
"request": "GET http://index.html HTTP/1.1",
"request-headers": "Host: example.com
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.3a5pre) Gecko/20100601 Minefield/3.7a5pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive",
"blocked-uri": "http://evil.com/some_image.png",
"violated-directive": "img-src 'self'",
"original-policy": "allow 'none'; img-src *; allow 'self'; img-src 'self'"
}
}
Table Definition for storing CSP Violations
Below is the MySQL table definition that we will use to store our CSP violations in.
CREATE TABLE `cspViolations` ( `request` varchar(1024) DEFAULT '', `headers` varchar(2048) DEFAULT '', `blockedUri` varchar(1024) DEFAULT '', `violatedDirective` varchar(1024) DEFAULT '', `originalPolicy` varchar(1024) DEFAULT '', `createdDateTime` datetime DEFAULT NULL, `ipAddress` varchar(16) DEFAULT NULL ) DEFAULT CHARSET=latin1;
PHP Example of capturing and storing CSP violations in a MySQL Database
Finally, an example that ties it all together. Remember, you’ll need to change your MySQL connection settings to match your database and have PHP 5.2 or greater for built in JSON support.
<?php
try {
// Parse JSON
$json = file_get_contents('php://input');
if ($json === FALSE)
throw new Exception('Bad Request');
// Decode JSON
$csp = json_decode($json, TRUE);
if (is_null($csp))
throw new Exception('Bad JSON Violation');
// Create a connection to MySQL Database
$mysqli = new mysqli('MyHost', 'MyUser', 'MyPassword', 'MyDatabase');
// Escape your data
$request = $mysqli->real_escape_string($csp['csp-report']['request']);
$headers = $mysqli->real_escape_string($csp['csp-report']['request-headers']);
$blocked = $mysqli->real_escape_string($csp['csp-report']['blocked-uri']);
$violated = $mysqli->real_escape_string($csp['csp-report']['violated-directive']);
$policy = $mysqli->real_escape_string($csp['csp-report']['original-policy']);
$ip = $mysqli->real_escape_string($_SERVER['REMOTE_ADDR']);
// Log Violations
$query = "
INSERT INTO
cspViolations
SET
request = '$request',
headers = '$headers',
blockedUri = '$blocked',
violatedDirective = '$violated',
originalPolicy = '$policy',
createdDateTime = NOW(),
ipAddress = '$ip'";
$mysqli->query($query);
$mysqli->close();
} catch(Exception $e) {
}
?>
