35

I noticed that GitHub and Facebook are both implementing this policy now, which restricts third party scripts from being run within their experience/site.

Is there a way to detect whether a document is running against CSP using JavaScript? I'm writing a bookmarklet, and want to give the user a message if they're on a site that doesn't support embedding a script tag.

AndrewF
  • 1,827
  • 13
  • 25
onassar
  • 3,313
  • 7
  • 36
  • 58
  • You can try to inject a new `` into the document and then check if the variable is set. If CSP is enforced (and your script is not allowed) this code block will not be executed at all. – kravietz Oct 29 '13 at 13:31
  • Interesting, will try that – onassar Oct 29 '13 at 16:11
  • Did it work? Just curious :) – kravietz Nov 04 '13 at 20:16
  • 2
    Naw :( There's no problem executing JS within Fb or those with content policies. Just in them loading 3rd party, non-whitelisted scripts. I may look into the `script` onerror event, and see if that can get me anywhere. – onassar Nov 04 '13 at 20:32
  • I tried the onerror method as @onassar suggested and it seems to work, at least in Chrome 40 – jrz Oct 01 '14 at 20:57
  • answer here https://stackoverflow.com/a/40053880/1784193 – milpool Jan 16 '20 at 02:28

7 Answers7

25

You can try to catch a CSP violation error using an event "securitypolicyviolation"

From: https://developer.mozilla.org/en-US/docs/Web/API/SecurityPolicyViolationEvent

example:

document.addEventListener("securitypolicyviolation", (e) => {
  console.log(e.blockedURI);    
  console.log(e.violatedDirective);    
  console.log(e.originalPolicy);
});
belykh
  • 1,109
  • 10
  • 25
8

From https://github.com/angular/angular.js/blob/cf16b241e1c61c22a820ed8211bc2332ede88e62/src/Angular.js#L1150-L1158, function noUnsafeEval

function noUnsafeEval() {
  try {
    /* jshint -W031, -W054 */
    new Function('');
    /* jshint +W031, +W054 */
    return false;
  } catch (e) {
    return true;
  }
}
Thomas Watson
  • 6,507
  • 5
  • 33
  • 43
brauliobo
  • 5,843
  • 4
  • 29
  • 34
6

What about this. For slow connections, the timeout should probably be raised. Onload is what I used to detect it and it seems to work. If it loads then CSP obviously isn't enabled or it is configured improperly.

var CSP = 0;
frame = document.createElement('script');
frame.setAttribute('id', 'theiframe');
frame.setAttribute('src', location.protocol+'//example.com/');
frame.setAttribute('onload', 'CSP=1;');
document.body.appendChild(frame);
setTimeout(function(){if (0 == CSP){alert("CSP IS ENABLED");}}, 250);
  • Interesting. Anything that would work regardless of the internet speed? – onassar Jan 09 '14 at 19:32
  • Not that I know of. I played with a lot of different types of mechanisms to detect CSP and none of them worked very well. This is by far the best that I discovered. In practice, I set the timeout fairly high and print a pretty general error message that, if the user gets the message often it may be due to the security settings of the page. –  Jan 10 '14 at 13:52
  • gotcha. would be strange to set a high one, as then i'd have to prevent my app/script from running until that timeout was met. thanks though. – onassar Jan 11 '14 at 17:01
  • gotcha. dont wanna count on it though. our bookmarklet is used pretty globally :/ – onassar Jan 12 '14 at 22:49
  • Aha. Mine is run in a globally used Bookmarklet too. I agree. My solution isn't great but I played around with a lot of things and this is the only one that works. I don't know what your bookmarklet does, but ours is pretty heavyweight, so this works well. If you do come across something better please let me know! Cheers –  Jan 13 '14 at 00:24
  • Can you link yours? A lot of our users come from developing nations with lower internet bandwidths – onassar Jan 13 '14 at 04:38
  • Whoops. I meant link your Chrome Web Store extension – onassar Jan 14 '14 at 17:16
  • I get `Refused to execute script because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.` Also after I set frame.setAttribute('type', 'application/javascript'); – Kiechlus Jan 11 '18 at 11:48
5

From https://hackernoon.com/im-harvesting-credit-card-numbers-and-passwords-from-your-site-here-s-how-9a8cb347c5b5:

fetch(document.location.href)
.then(resp => {
  const csp = resp.headers.get('Content-Security-Policy');
  // does this exist? Is is any good?
});

This will fail however with connect-src='none' and be reported.

Kiechlus
  • 1,167
  • 12
  • 21
4

An easy way to detect support for CSP is just by checking if JavaScript's eval()-method can be run without throwing an error, like so:

try {
    eval("return false;");
} catch (e) {
    return true;
}

However, this only works if CSP is actually turned on (obviously), with Content-Security-Policy being set in the response headers the page loaded with, and without 'unsafe-eval' in script-src.

I came here looking for a way to detect CSP support in browsers without CSP actually being turned on. It would seem this is not possible though.

On a side note, IE does not support CSP, only the sandbox directive in IE 10+, which, by looking at the CSP standard, does not make it a conformant web browser.

3

Currently, there is no way to do so in shipping browsers.

However, something such as the following should work, per spec, and does in Chrome with experimental web platform features enabled in chrome://flags/:

function detectCSPInUse() {
  return "securityPolicy" in document ? document.securityPolicy.isActive : false;
}

The SecurityPolicy interface (what you get from document.securityPolicy if it is implemented) has a few attributes that give more detail as to what is currently allowed.

gsnedders
  • 5,532
  • 2
  • 30
  • 41
  • I ran that code in Facebook via the console. Got a `false` even though they're running one (which prevents inserting `script` tags) – onassar Oct 27 '13 at 01:12
  • This feature doesn't look like it's quite ready yet. For example, in all current versions of Chrome, this feature is hidden behind the **Enable experimental Web Platform features** flag. Your users would have to go to `chrome://flags/`, turn it on, and restart their browser. However, you can implement it now -- the code above is designed to work if the API is available. – AndrewF Oct 27 '13 at 23:38
  • This feature seems to have disappeared from Chrome, even with chrome://flags/#enable-experimental-web-platform-features turned on. – Jeffrey Yasskin Apr 22 '14 at 05:37
  • 3
    @JeffreyYasskin It would appear the `SecurityPolicy` interface is no more in the spec. – gsnedders Apr 23 '14 at 23:02
0

I am checking onError event in my bookmarklet code and prompt a user to install my extension if script is not loaded.

javascript:(function(){
var s=document.createElement('script');
s.setAttribute('type','text/javascript');
s.setAttribute('src','https://example.ru/bookmarklet?hostname=%27+encodeURIComponent(location.hostname));
s.setAttribute('onerror', 'if(confirm(`Downloading from the site is possible only through the "MyExtensionName" extension. Install extension?`)){window.open("https://chrome.google.com/webstore/detail/myextensionlink");}');
document.body.appendChild(s);})();
Evgeny Ivanov
  • 504
  • 6
  • 14