Content Security Policy (CSP) is a security mechanism that helps prevent cross-site scripting (XSS), clickjacking, and other code injection attacks by allowing websites to specify which sources of content are trusted. It is an HTTP header that defines a whitelist of content sources, such as scripts, stylesheets, and images, that a website is allowed to load and execute.
When a web page with a CSP is loaded, the browser will only load resources from the allowed sources specified in the policy. If a resource is not from an allowed source, the browser will block it from loading. This helps to protect against malicious code injection attacks that try to load unauthorized scripts or other resources on the page.
CSP can be customized to meet the needs of different websites by allowing or blocking specific sources of content, such as third-party scripts, inline scripts, or eval functions. It can also be used to report violations to a designated endpoint, which can help developers identify and fix security issues.
Overall, CSP is a powerful tool for improving the security of web applications by reducing the attack surface and mitigating the impact of code injection attacks.
It is a feature of modern browsers, allows to helps protect the application.
- An added layer of protection that helps mitigate some client-side attacks. Works similar to a firewall, for client-side source code.
- Lets developers whitelist client-side code to run on their website.
Limitations
- opt-in model; does nothing by default. Developers have to explicitly specify.
- only an exposure control; CSP cannot patch vulnerabilities or remediate defects. Not a substitute for output encoding and other primary controls. Does not fix anything and is used like a blocker to block contents. CSP is not a fix for vulnerabilities in code. It’s a mechanism that limits content sources and helps to mitigate the impact of code injection attacks. It’s important to use CSP alongside other primary security controls like input validation and output encoding for a more comprehensive security solution.
- only partially effective; does not stop HTML code injection, although it can limit the effectiveness of HTML injection by blocking images, actions, etc.
- only helps with client-side attacks; useless for server-side vulnerabilities. It is basically a browser-based control.
Challenges
It requires developers with best-practice code. CSP is basically a policy that blocks the injection.
Developers are allowed to disable CSP. Might implement CSP technically but in unsafe (disabled when CSP is not wanted) mode.
CSP forces to write best-practice code. Code cannot be embedded in HTML. All code including JS and styles must be placed in “include” files.
CSP restricts inline (CSS) and inline (JS). Events must migrate to registered events.
Any remaining scripts must be tagged using “nonce” or “digest”
Lets the browser know that this code is a part of the source code. The developer team understands that it was not injected at runtime by some untrusted sources.
- Requires developers to write best-practice code
- Developers can disable CSP or use it in unsafe mode
- Forces developers to place code in separate files rather than embedding in HTML
- Restricts the use of inline CSS and JS
- Events must be migrated to registered events
- Any remaining scripts must be tagged using “nonce” or “digest”
- Developers must ensure that the code is part of the source code and not injected at runtime by untrusted sources.
Example
$CSPNonce = bin2hex(openssl_random_pseudo_bytes(32));
$lcsp = "Content-Security-Policy: script-src 'self' nonce {$CSPNonce} mutillidae.local;"
......
......
<script nonce="{$CSPNonce}">
document.addEventListener('DOMContentLoaded', function(){
document.getElementById('idCSPForm')....
This code is implementing Content Security Policy (CSP) on a web page by using a nonce to allow only specific scripts to be executed. Here’s what’s happening in the code:
- The first line generates a random 32-byte nonce using the
openssl_random_pseudo_bytes()
function and converts it to a hexadecimal string usingbin2hex()
. - The second line constructs a CSP header with a policy that allows scripts to be loaded only from the current domain (
'self'
) and from the domainmutillidae.local
. Thenonce
attribute is included with the nonce value from the previous step to allow specific scripts to bypass the CSP policy. - The third section of the code includes a script tag with the
nonce
attribute set to the nonce value. This allows the script to bypass the CSP policy and be executed because it has a matching nonce. - The
addEventListener
function sets up a listener for theDOMContentLoaded
event, which fires when the page has finished loading. When this event is triggered, the function passed to it will execute. In this case, the function is searching for an element with the IDidCSPForm
and performing some action on it.
Overall, this code is using CSP to limit the sources of scripts that can be loaded on the page, which can help to mitigate the risk of code injection attacks. The use of a nonce allows specific scripts to bypass the CSP policy, while still ensuring that they are not injected at runtime by untrusted sources.
Implementation
Implemented as HTTP Response Header, dictating the site’s security policy.
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Security-Policy: default-src 'self'; script-src 'self' <https://trustedcdn.example.com>; style-src 'self' 'unsafe-inline'
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=31536000; includeSubDomains
Date: Fri, 30 Apr 2023 00:00:00 GMT
Content-Length: 1234
Like all HTTP Headers, CSP header is a colon-delimited name and value. Content-Security-Policy (CSP) is the standard header used to specify a content security policy for a web page. X-Content-Security-Policy is an older, deprecated version of the CSP header that lacks some of its advanced features. CSP is more robust and flexible, and recommended for modern browsers, while X-Content-Security-Policy may still be needed for backward compatibility.
The policy can be broken down into multiple directives. Values are basically a set of one/more directives separated by semi-colons.
Directives
Directives are the one who blocks. They are of multiple types –
Type | Directive | What it does | Example |
---|---|---|---|
Blockers | resource ‘fetch’ policies | specifies trusted locations for source code and other resources. From where data is allowed to fetch. | script-src ‘self’ (allowing scripts to only be loaded from the same domain as the page itself) |
Blockers | document policies | allow further control over the behavior of a document, including the application of CSP rules, beyond what is specified in the HTTP response headers. | allow-top-navigation ‘self’ (allowing the page to be navigated to other pages on the same domain) |
Blockers | navigation policies | from/to where users can navigate | frame-ancestors ‘none’ (preventing the page from being loaded inside an iframe on another domain) |
Reports | reporting policies | allows violations to be reported to the development teams. | report-uri /csp-violation-report-endpoint (specifying a URL where violation reports should be sent) |
The composition of CSP is in name:value pairs. Directives have a name and a list of sources, which are space delimited.
Example Names | Example Sources |
---|---|
script-src, style-src, default-src | -none, -self, -url, [-nonce, -digest] – code markers |
This policy defines that, scripts can be downloaded from ‘self’ (same origin) and the URL.
There are different types of sources –
- none – Disables any resources for the policy
- self – content from the same domain or the origin
- URL – another domain
- nonce – a random number assigned to the script by the server [dynamic]
- digest – a digest (hash) of the code included in the page [static]
nonce and digests are used to tag legacy code so the code won’t be blocked.
CSP has brought formerly independent features under the same umbrella. It has introduced directives related to HTML Injection. It does not directly block; but blocks activities that typically go along with HTML Injection.
- form-action: defines valid sources that can be used as an HTML <form> action.
- font-src: restricts source (URLs) from which fonts may be loaded.
- img-src: restricts URLs from which images may be loaded.
- style-src: restricts locations from which style may be applied to a document.
- frame-src: defines valid sources for loading frames.
- media-src: restricts URLs from which media resources may be loaded.
- object-src: restricts URLs from which objects may be loaded.
- sandbox: enables a set of extra restrictions on any content hosted by the frame and iframe.
CSP does not block any HTML Injection but rather blocks different actions.
Important Directives
#Directives Related to XSS
Directive | What it does |
---|---|
connect-src | Restrict URLs loaded using script interfaces like XHR and web sockets. Where this domain is allowed to connect to. |
script-src | Restricts the locations from which scripts may be executed |
These directives may mitigate code injection. Some sample console.logs for CSP Blocking –
Refused to load the script '[<http://example.com/malicious.js>](<http://example.com/malicious.js>)' because it violates the following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
Refused to load the script '*script-uri*' because it violates the following Content Security Policy directive: "*your CSP directive*".
Content Security Policy: A violation occurred for a report-only CSP policy ("An attempt to execute inline scripts has been blocked"). The behavior was allowed, and a CSP report was sent.
In addition to a console message, a securitypolicyviolation
event is fired on the window. Further: https://www.w3.org/TR/CSP2/#firing-securitypolicyviolationevent-events.
In general, all the blocking directives work similarly.
CSPs’ XSS directives obsolete X-XSS-Protection. This is replaced by script-src policy.
#Directives Related to Phishing
Directive | What it does |
---|---|
frame-ancestors | defines valid sources to embed resources. using , , <object>, <embed>, <applet>. setting this directive to ‘none’ is equivalent to X-Frame-Options: DENY. Blocks potential clickjacking.</p> |
navigate-to | Restricts where document may send the user. Checks the link, if it is the designated one or not. example: link clicked → form submitted (on) window.location. form-action takes precedence over form submission. |
form-action | valid sources that can be used as an HTML <form> action. Similar to navigate-to, decides where the form submission can be sent to. |
Obsoletes X-Frame-Options. frame-options is replaced by frame-ancestors policy.
frame-ancestors extend the capability of frame-options. Systems that want to allow others (example: business partners) to frame the site, might include the following HTTP Header where <other domain> is a fully qualified domain without wild cards.
#Special Directives
default-src: Serves as a fallback for other fetch directives. Sets the policy for any directives that are not specified.
#Reporting Features
Reporting directives basically report back whenever there is a problem.
Directive | What it does |
---|---|
report-uri | instructs the user-agent to report violations to the given URL |
report-to | will replace report-uri. declares report policy in ‘report-to’ HTTP Header. Indicates which reporting endpoint from the report-to to use. |
The Report-to
HTTP header and report-to
CSP directives are both used to report violations of the Content Security Policy (CSP) to a designated endpoint. However, there are some differences between the two.
The Report-to
HTTP header allows website owners to specify an endpoint URL to which the user agent can report violations of the CSP policy. This header is sent along with the CSP policy in the HTTP response. Here’s an example of how it can be used:
Content-Security-Policy: script-src 'self'; report-to [<https://example.com/csp-report-endpoint>](<https://example.com/csp-report-endpoint>)
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"[<https://example.com/csp-report-endpoint"}],"include_subdomains>](<https://example.com/csp-report-endpoint%22%7D%5D,%22include_subdomains>)":true}
The report-to
CSP directive is used to specify a JSON object that defines the endpoint(s) to which the user agent should report CSP violations. This directive is included in the CSP policy itself. Here’s an example:
Content-Security-Policy: script-src 'self'; report-to csp-endpoint;
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"<https://example.com/csp-report-endpoint"}],"include_subdomains>":true}
In both cases, the JSON object specifies the endpoint URL to which the user agent should report the violations. However, with the Report-to
HTTP header, the report-to
directive is not necessary in the CSP policy itself. Additionally, the Report-to
HTTP header allows for the specification of multiple endpoints, while the report-to
directive only allows for one endpoint to be specified.
Overall, the Report-to
HTTP header and report-to
CSP directive provides similar functionality for reporting CSP violations, but the Report-to
HTTP header provides more flexibility in specifying multiple endpoints and allows for reporting on policies that do not include a report-to
directive.
Additional Resources
- https://content-security-policy.com/
- https://portswigger.net/web-security/cross-site-scripting/content-security-policy
Conclusion
Content-Security-Policy is a security mechanism that helps defend web applications from a variety of attacks, such as cross-site scripting (XSS), clickjacking, and other code injection attacks. It enables developers to whitelist specific content sources and restrict others, thereby reducing the attack surface for malicious actors. However, CSP has certain limitations and must be implemented with caution in order to be effective.
In the subsequent blog post, we will discuss various techniques that attackers can use to bypass CSP as well as other offensive approaches.