Content-Security Policy (CSP)
A Content Security Policy is a defense-in-depth security measure that can be used to lower the severity of Cross-Site Scripting (XSS) vulnerabilities by limiting their exploitability. It is configured in the Content-Security-Policy response header.
CSP Basics
A CSP consists of multiple directives. Each directive allows one or more values. The browser enforces the CSP and prevents the loading or execution of resources depending on the CSP. We will discuss a few sample directives in this section.
For instance, the script-src directive defines where JavaScript can be loaded and executed from; we can limit the domains JavaScript code is allowed to be loaded from using the following policy:
Content-Security-Policy: script-src 'self' https://benignsite.htb
This tells the browser to only load JavaScript from the same origin as the page itself and the external origin https://benignsite.htb. Therefore, if an attacker injects the following JavaScript code in an XSS payload, the victim's browser will not load the script and thus not execute it:
<script src="https://exploitserver.htb/pwn.js"></script>
However, the following scripts are allowed to load and execute:
<script src="/js/useful.js"></script> <script src="https://benignsite.htb/main.js"></script>
Furthermore, since the unsafe-inline value is not specified, it blocks all inline scripts. Therefore, the following potential XSS payloads are all blocked and thus not executed:
<script>alert(1)</script> <img src=x onerror=alert(1) /> <a href="javascript:alert(1)">click</a>
Additionally, there are other common directives:
style-src: allowed origins for stylesheetsimg-src: allowed origins for imagesobject-src: allowed origins for objects such as<object>or<embed>connect-src: allowed origins for HTTP requests from scripts. For instance, usingXMLHttpRequestdefault-src: fallback value if a different directive is not explicitly set. For instance, if theimg-srcis not present in the CSP, the browser will use this value instead for imagesframe-ancestors: origins allowed to frame the page, for instance, in an<iframe>. This can be used to prevent Clickjacking attacksform-action: origins allowed for form submissions
For additional CSP directives, check out the list provided here.
Additional values for directives include:
*: All origins are allowed'none': No origins are allowed*.benignsite.htb: All subdomains ofbenignsite.htbare allowedunsafe-inline: Allow inline elementsunsafe-eval: Allow dynamic code evaluation such as JavaScript'sevalfunctionsha256-407e1bf4a1472948aa7b15cafa752fcf8e90710833da8a59dd8ef8e7fe56f22d: Allow an element by hashnonce-S0meR4nd0mN0nC3: Allow an element by nonce
For additional CSP directive values, check out the list provided here.
Secure CSPs
Making the CSP as strict as possible is crucial to securing a web application. This can be achieved by starting from a strict baseline CSP and gradually loosening restrictions until the web application works as intended. A good baseline CSP is the following:
Content-Security-Policy: default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self'; frame-ancestors 'self'; form-action 'self';
This CSP only allows the loading of images, stylesheets, and scripts from the same origin, only allows HTTP requests from JavaScript and form submissions to the same origin, only allows the same origin to frame the web page, and prevents any other resource from loading. The CSP needs to be adjusted accordingly if any external resources are used.
Additionally, the inline JavaScript code the web application uses must be removed to prevent it from being blocked. This can be easily achieved by moving it to a script file and loading it. For instance, consider the following inline JavaScript code:
<script>
var poc = "test";
function submitForm(){
console.log(poc);
}
</script>
<button id="submit" onclick="submitForm()">This is functionally identical to creating a file test.js with the following content:
var poc = "test";
function submitForm(){
console.log(poc);
}
document.getElementById("submit").addEventListener('click', submitForm);And then loading the script:
<script src="/test.js"></script>
This way, all inline JavaScript code can be removed.
We can use available online tools to evaluate a CSP for us, such as the CSP Evaluator provided by Google. For more details on how to write a secure CSP, check out the OWASP CSP Cheat Sheet.
Last updated