TL;DR
A permissive Content Security Policy (CSP) lets you see what resources your website would block, without actually blocking them. This is incredibly useful for identifying issues and gradually tightening security. We’ll show how to set it up, interpret the reports, and move towards a stricter policy.
1. Understanding CSP Reports
CSP reports tell you when a resource would have been blocked by your policy. They are sent to a URL you specify. These reports are vital for debugging. They contain information like:
- blocked-uri: The URL that was attempted to be loaded.
- document-uri: The page where the block would have occurred.
- effective-directive: The CSP directive that caused the block (e.g.,
script-src).
2. Setting up a Permissive Policy
Start with a very relaxed policy to gather information. This means allowing everything. Add this header to your web server configuration:
Content-Security-Policy: default-src 'self'
This allows resources from the same origin as your website. It’s not secure, but it’s a starting point.
3. Configuring Report Reporting
Tell your browser where to send CSP violation reports. Add this header:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
This sends reports to the /csp-report endpoint on your server, without blocking anything.
4. Creating a Report Endpoint
You need a script or application to handle incoming CSP reports. The report will be sent as a JSON payload in a POST request. Here’s a simple Python (Flask) example:
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/csp-report', methods=['POST'])
def csp_report():
data = request.get_json()
# Log the report data (e.g., to a file or database)
print(data)
return jsonify({'status': 'received'})
if __name__ == '__main__':
app.run(debug=True)
Remember to replace print(data) with proper logging.
5. Analysing Reports
Examine the reports sent to your endpoint. Look for:
- Unexpected blocked URIs: Resources you didn’t realize were being loaded from external sources.
- Common blocking patterns: Repeated blocks suggest a widespread issue (e.g., inline scripts).
6. Gradually Tightening the Policy
Once you understand your website’s resource loading, start making the policy stricter:
- Specific script sources: Instead of
script-src 'self', allow only known scripts:Content-Security-Policy: script-src 'self' https://trusted.cdn.com - Nonce or Hash for inline scripts: If you must use inline scripts, use a nonce (random value) or hash.
Content-Security-Policy: script-src 'nonce-{random_value}' - Style sources: Control CSS loading similarly to scripts.
- Image sources: Restrict image loading domains.
7. Testing in Production
Before fully enforcing a strict policy, use Content-Security-Policy-Report-Only for a period of time to monitor its impact on real users.
8. Enforcing the Policy
When you’re confident, switch from Content-Security-Policy-Report-Only to Content-Security-Policy with your final policy:
Content-Security-Policy: script-src 'self' https://trusted.cdn.com; object-src 'none'; ...