Whilst using CSP for a slightly different purpose (sandboxing) I realized that a very simple auto clicked link seems to bypass even relatively strict CSP. What I am describing is the following:
Content security policy:
default-src 'none'; script-src 'unsafe-inline';
And the body:
<a href="">test</a>
<script>
document.querySelector("a").click();
</script>
Obviously in a real attack you would include the cookie information into the href
field first and probably wrap this in a hidden self-embedding iframe or make the domain redirect you back to where you came from (potentially with additional url parameters thus creating a sort of XMLHttpRequest bypassing connect-src
), but this basic example does show the problem.
Is there any way to prevent this with CSP (which still does allow the execution of Javascript)?
Similar attacks
The same thing can obviously be done with some other navigation methods as well. The reason why I was asking specifically about this method actually has more to with my secondary goals than XSS exploits. Either way, open to any and all real solutions.
Irrelevant side explanation
Because of all the confusion how this could still be applicable even without script-src: 'unsafe-inline'
. Imagine the following file named api.ext
print URLParameters.method
[...]
This file could then be called like api.ext?method=<script src='api.ext?method=alert("test")//'></script><!--
(except you would need additional URL encoding and stuff, this is just to get the point across). Finding exploits like this is hard and they are quite rare, but things like connect-src
seem to exist to prevent leakage of information even in those cases.
Whilst using CSP for a slightly different purpose (sandboxing) I realized that a very simple auto clicked link seems to bypass even relatively strict CSP. What I am describing is the following:
Content security policy:
default-src 'none'; script-src 'unsafe-inline';
And the body:
<a href="http://www.google.">test</a>
<script>
document.querySelector("a").click();
</script>
Obviously in a real attack you would include the cookie information into the href
field first and probably wrap this in a hidden self-embedding iframe or make the domain redirect you back to where you came from (potentially with additional url parameters thus creating a sort of XMLHttpRequest bypassing connect-src
), but this basic example does show the problem.
Is there any way to prevent this with CSP (which still does allow the execution of Javascript)?
Similar attacks
The same thing can obviously be done with some other navigation methods as well. The reason why I was asking specifically about this method actually has more to with my secondary goals than XSS exploits. Either way, open to any and all real solutions.
Irrelevant side explanation
Because of all the confusion how this could still be applicable even without script-src: 'unsafe-inline'
. Imagine the following file named api.ext
print URLParameters.method
[...]
This file could then be called like api.ext?method=<script src='api.ext?method=alert("test")//'></script><!--
(except you would need additional URL encoding and stuff, this is just to get the point across). Finding exploits like this is hard and they are quite rare, but things like connect-src
seem to exist to prevent leakage of information even in those cases.
- 1 You might want to hover your mouse over the csp tag.. there's too many TLAs (three-letter acronyms), and you've chosing the wrong one... – Marc B Commented Apr 20, 2015 at 15:52
-
2
How could this be done without
unsafe-inline
? – levi Commented Apr 20, 2015 at 16:47 -
@levi If you have a XSS exploit where you control the full content from the first character (rare, but not unseen) you can simply self-embed in the outer embed and have your javascript in the inner embed. But either way, that was just a side note, the existence of
connect-src
shows that CSP also cares about Javascript evaluation XSS exploits. – David Mulder Commented Apr 20, 2015 at 19:16 - @MarcB Ah, my bad, I was careless, got it correctly on my other post, not sure why I mistagged on this one. – David Mulder Commented Apr 20, 2015 at 19:22
-
1
@JacobBundgaard If you would have read the entire question or the ments you would have seen that 1) Even without
unsafe-inline
the attack can still be executed and 2) that CSP is concerned with such attacks as proven by the existence ofconnect-src
. – David Mulder Commented Apr 26, 2015 at 15:35
3 Answers
Reset to default 2 +50This is unlikely going to be a satisfying approach - and obviously it isn't based on CSP - but it might be your only option if you really have to prevent such attacks. Before using anything like this, make sure that there is really no way to disable inline scripts (which should cover most attacks). Moreover, you should send your feedback to the [email protected] mailing list with a [CSP2] subject.
Here my (inplete) idea:
function guardMethods(clazz, methodNames, urlGetter, allowFilter, reportViolation) {
var prototype = clazz.prototype;
methodNames.forEach(function (methodName) {
var originalMethod = prototype[methodName];
if (originalMethod) {
Object.defineProperty(prototype, methodName, {
value: function () {
var url = urlGetter.apply(this, arguments) || '';
if (allowFilter(url)) {
return originalMethod.apply(this, arguments);
} else {
reportViolation(url);
}
}
});
}
})
}
function allowFilter(url) {
// todo: implement
}
function reportViolation(url) {
console.error('Redirection prevented:', url);
}
guardMethods(HTMLAnchorElement, ['click', 'dispatchEvent', 'fireEvent'], function () {return this.href}, allowFilter, reportViolation);
You would have to implement similar guards for location, location.href, window.open and other functions/properties/events which allow to redirect to other pages. If you miss just one, then you are still vulnerable. Forms, XHR and most other resources can be covered with CSP itself. As far as I know, the prototype hack does not work in some older browsers.
Once more, I do not remend to use this. The chance that you make a mistake or that it does not work in some browsers or that a new API will be added which can be leveraged for redirects is just too high.
CSP is one of the ways to reduce the damage done by XSS, but it is by no means a magic wand that fixes all issues caused by XSS vulnerabilities. This non-goal is also listed explicitly in the CSP specification:
Content Security Policy (CSP) is not intended as a first line of defense against content injection vulnerabilities. Instead, CSP is best used as defense-in-depth, to reduce the harm caused by content injection attacks. As a first line of defense against content injection, server operators should validate their input and encode their output.
If you have to run JavaScript code, but you cannot trust the code, then you could serve the page with a sandbox directive without the allow-same-origin
flag. With this CSP directive, the page will run at a unique security origin, which does not share any state (i.e. cookies, DOM Storage, databases, ...) with the displayed origin. Consequently, injected scripts cannot leak information, because they cannot get the information in the first place.
For example, to allow inline scripts to run, but without access to same-origin data, use:
default-src 'none'; script-src 'unsafe-inline'; sandbox allow-scripts
Don't bother with blacklisting JavaScript methods as suggested by another answer, because 1) you will always overlook a method and 2) disabling JavaScript APIs may break your web application in unexpected ways, and 3) the attacker only needs one hole to do damage, and your (custom) application probably contains at least one method which can be abused by an attacker.
Content Security Policy is for the security of the page itself. Navigating to another page is not a bypass or something that concerns CSP. CSP is only concerned with your page and what it can do. It's also not about restricting the utility of the browser for the end user (like the ability to install plugins or open links).
default-src 'none';
This tightens the policy to allow no XHR / Fetch / WebSockets / CSS / Font / JavaScript / Plugin content from anywhere. These all have their respective properties but in their absence the default property is used. You have not attempted to do any of these in your javascript.
script-src 'unsafe-inline';
This relaxes the policy to allow any javascript that is embedded into the page to be used. This includes onclick/onhover and that whole family of unsafe attributes. To quote the spec:
In either case, authors SHOULD NOT include either 'unsafe-inline' or data: as valid sources in their policies. Both enable XSS attacks by allowing code to be included directly in the document itself; they are best avoided pletely.
Instead of this, if you feel the need to embed content in the document itself for whatever reason, there are hash and nonce values that can be placed in your policy string to whitelist your inline scripts.