Understanding CSP Violation Reports
Understanding CSP Violation Reports
CSP violation reports provide crucial insights into policy effectiveness and potential security threats. Understanding how to interpret these reports forms the foundation of effective CSP debugging. Each violation report contains specific information that helps identify the source and nature of the policy breach.
A typical CSP violation report structure:
{
"csp-report": {
"blocked-uri": "https://evil-script.com/malicious.js",
"column-number": 12,
"disposition": "enforce",
"document-uri": "https://example.com/page.html",
"effective-directive": "script-src",
"line-number": 8,
"original-policy": "default-src 'self'; script-src 'self' https://trusted.com",
"referrer": "https://example.com/",
"script-sample": "eval('malicious code')",
"source-file": "https://example.com/app.js",
"status-code": 0,
"violated-directive": "script-src"
}
}
Building a comprehensive violation monitoring system:
// CSP Violation Logger and Analyzer
class CSPViolationAnalyzer {
constructor(options = {}) {
this.violations = [];
this.patterns = new Map();
this.thresholds = options.thresholds || {
sameViolation: 10,
timeWindow: 300000 // 5 minutes
};
}
analyzeViolation(report) {
const violation = report['csp-report'];
const key = this.generateViolationKey(violation);
// Track violation patterns
if (!this.patterns.has(key)) {
this.patterns.set(key, {
count: 0,
firstSeen: Date.now(),
lastSeen: Date.now(),
samples: []
});
}
const pattern = this.patterns.get(key);
pattern.count++;
pattern.lastSeen = Date.now();
pattern.samples.push(violation);
// Analyze for potential attacks
return {
isLikelyAttack: this.detectPotentialAttack(pattern),
isKnownResource: this.checkKnownResources(violation),
severity: this.calculateSeverity(violation, pattern),
recommendation: this.generateRecommendation(violation)
};
}
generateViolationKey(violation) {
// Create unique key for grouping similar violations
return `${violation['violated-directive']}:${violation['blocked-uri']}:${violation['source-file']}`;
}
detectPotentialAttack(pattern) {
// High frequency of violations in short time window
const timeWindow = Date.now() - pattern.firstSeen;
const rate = pattern.count / (timeWindow / 1000); // violations per second
return rate > 0.1 || pattern.count > this.thresholds.sameViolation;
}
checkKnownResources(violation) {
const knownDomains = [
'googleapis.com',
'googletagmanager.com',
'google-analytics.com',
'cloudflare.com',
'jsdelivr.net',
'unpkg.com'
];
const blockedUri = violation['blocked-uri'];
return knownDomains.some(domain => blockedUri.includes(domain));
}
calculateSeverity(violation, pattern) {
let severity = 'low';
if (violation['violated-directive'].includes('script-src')) {
severity = 'high';
} else if (violation['violated-directive'].includes('connect-src')) {
severity = 'medium';
}
if (pattern.count > 100) {
severity = 'critical';
}
return severity;
}
generateRecommendation(violation) {
const directive = violation['violated-directive'];
const blockedUri = violation['blocked-uri'];
if (this.checkKnownResources(violation)) {
return {
action: 'consider-allowing',
suggestion: `Add '${new URL(blockedUri).origin}' to ${directive}`,
risk: 'low'
};
}
if (blockedUri.includes('eval') || blockedUri.includes('inline')) {
return {
action: 'refactor-code',
suggestion: 'Replace inline scripts with external files or use nonces',
risk: 'high'
};
}
return {
action: 'investigate',
suggestion: 'Review the source and determine if this resource is legitimate',
risk: 'medium'
};
}
}