Important information about Cordova 5

This post is more than 2 years old.

In the most recent update to Apache Cordova, there was a rather important change that could really confuse you if you aren't paying attention. This is exactly the type of thing that I would have warned my readers about, but I mistakenly thought it would not impact most users. I'll explain later why I screwed that up, but I want to give huge thanks to Nic Raboy and his post, Whitelist External Resources For Use In Ionic Framework. Nic is a great blogger that I recommend following, and it is his post that led me to dig more into the changes in Cordova 5 and do my own research.

I won't repeat Nic's post here, but the summary is that how you whitelist in Cordova has changed from earlier versions. Previously whitelisting was done via an <access> tag in config.xml. The default application created by the CLI would use a * to make everything available. To repeat, by default you could use any resource in your Cordova app.

In Cordova 5, this was changed. Specifically, this was changed for Android and iOS. You can begin by looking at the whitelist guide from the Cordova docs, but this will lead you to the docs for the new whitelist plugin.

So let's talk about this plugin. If you create a new project using the default template, then this plugin is automatically added whenever you add a platform. What does this plugin do? For modern Android (KitKat and above) and all iOS versions (all supported) it uses a new security system called Content Security Policy (CSP for short). The best place to read about CSP is at MDN (https://developer.mozilla.org/en-US/docs/Web/Security/CSP). I'll do my best to explain it here though.

CSP is implemented via a meta tag in your HTML. Again, not your config.xml file but your actual HTML. This is what you'll see in the HTML file from the default template:

<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">

That's pretty weird looking, right? What you are basically seeing is a set of rules that dictate what resources can be loaded and how. You can split the above content by semicolons:

default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'
style-src 'self' 'unsafe-inline'
media-src *

The beginning of each part represents a "policy directive", basically "what my security rule applies to". So for example, media-src represents audio and video tags. style-src represents style sheets. There's more policy directives (including script-src) to give you really fine grained control over all aspects of your application. You can find the complete list here. default-src represents a default value but it only applies to policy directives that end with -src. If that sounds confusing, wait, I'm going to make it a bit more confusing in a bit.

So that's the policy directive, what about the values after it? These values dictate what locations particular resources may be loaded from. You can use a combination of keywords, like 'self', and URLs. Let's talk keywords first. The keyword 'self' means you can use any resource served from the same location as the current document. I can't imagine a case where you wouldn't want that, but it's possible. You can use 'none' to say nothing at all is allowed. A complete list of keywords may be found here.

URLs can be of the form "scheme", ie "http:" or a scheme and domain, like http://www.cnn.com. In my testing, I was not able to use a domain by itself nor was I able to use a wildcard for the scheme. Curious about gap? This is a special scheme for iOS and must be left there.

unsafe-eval isn't really a location but instead represents being able to use eval() within code. I've seen some JavaScript frameworks that require this so it is probably good that it is there by default. One more that isn't in the default template is unsafe-inline. This is a big one. Without this being in your CSP you can't use JavaScript code in your index.html file.

Now - I know all of us are good JavaScript developers and always put your code in JS files, but I know I've used inline JavaScript from time to time. Heck, on this blog I'll do it a lot just to keep the code a bit simpler. Well, this will no longer work unless you specifically modify the CSP to add unsafe-inline. To be honest, I'd skip that and just move your code into a JavaScript file. Note that the default CSP does allow inline style sheets.

Let's consider a simple example. I created a new application and then added a CDN copy of jQuery:

<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>

To be clear - I do not recommend this. If you do this and your app is offline than your entire application is screwed.

I then used this code in my deviceReady block:

$.get("http://www.cnn.com", function(res) {
    console.log(res);
    $("h1").html("set to cnn");
}

Out the gate, none of this will work. You can see this yourself in your remote inspector:

shot1

First, I need to update my CSP to allow a script src at code.jquery.com:

script-src 'self' http://code.jquery.com

Notice I added 'self'! I had thought that default-src including 'self' would cover this, but it does not. As soon as I added script-src, I needed to also add 'self' here to let local scripts work.

Correcting that lets jQuery load - but guess what - there's more:

shot2

What's nice is that the error is actually pretty descriptive. In a lot of security things in the browser I've seen things silently fail so this is a big help. It is telling you that you either need to set permission in default-src, or use the policy directive connect-src. connect-src is what you want here and applies to XHR, WebSocket, and EventSource directives. Here is what I added:

connect-src http://www.cnn.com<

So... make sense? Let's get a bit more particular. First off, what happens if you screw up your CSP? Imagine the following:

<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; script-src 'self' http://code.jquery.com connect-src http://www.cnn.com">

See the error? Maybe you don't - that's the point. When running, you will get an error in the console:

shot3

I'm shocked - like seriously shocked - how darn helpful that error is. In many cases, I've seen browsers simply "swallow" security issues and say nothing. This one not only noted a syntax issue but pretty much told you exactly how to fix it. In case your curious, Google's Android debug is just as helpful:

shot4

Now let me explain why I didn't think this post was necessary. I had read about the changes, but did not think they applied by default. I was confused because I explicitly do not use the default Cordova template. Since my template did not include a CSP tag, it didn't effect me! So I began to check on this and look at the different permutations.

If you do not include the plugin and do not include the CSP, you have no access to anything.

If you do not include the plugin and do include the CSP, you have no access to anything.

If you include the plugin and a CSP, you have access to what CSP gives you access to.

If you include the plugin and do not include a CSP, your access falls back to the access tag in config.xml, which is probably * (i.e. everything allowed).

My recommendation? Use the plugin and use the CSP. It is more work and you will screw it up, trust me, but you want to do the right thing. (And later this week I'll edit my normal default Cordova template so I can practice what I preach.)

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Nic Raboy posted on 5/25/2015 at 2:38 PM

Well done dude!

Comment 2 by Victor posted on 5/26/2015 at 8:16 AM

Very interesting, thanks a lot! When I saw about this "CSP" stuff i tried to ignore it because it looks really gibberish (all in a single line... yuck!) but now I see it will be important.
However, now I tried and it seems like mailto: and tel: schemes do not work in this CSP and must be used like in the old version, by <acces launch-external="yes" origin="mailto:"/> and <access launch-external="yes" origin="tel:"/>

Comment 3 by Nicolas Grolleau posted on 5/27/2015 at 2:03 PM

If you use the cordova-plugin-whitelist plugin and not configure CSP, you'll get plenty (like a lot) of lines in the logs asking to add CSP in the html files.

One important thing if you add CSP is to test your app with a device (or simulator) that really supports it.
When I migrated my app to cordova 5, I configured CSP mostly with 'self', tested on my primary target device wich runs android jelly bean and thought I was done.
But later people tested with KitKat and nothing was working.

I'd want to add that cordova-plugin-whitelist plugin still needs to be configured in config.xml (with access origin, allow-intent or allow-navigation) in addition to CSP in html.

An alternative for people wanting to migrate quicker and not focus on new security oportunities is to add cordova-plugin-legacy-whitelist instead (then security works like with older versions)

And I found CSP was pretty well explained on this page : http://content-security-pol...

Comment 4 (In reply to #3) by Raymond Camden posted on 5/27/2015 at 4:30 PM

Thanks for sharing this, Nicolas!

Comment 5 (In reply to #2) by Victor posted on 5/28/2015 at 7:20 AM

Wrong! - well, kinda.
From the cordova whitelist plugin documentation, I was missing <allow-intent href="tel:*"/> and the same for mailto:

If you use <access origin=""> tags for tel: and mailto: it looks like you also have to add <access origin="*"> for your app to access external requests.

Comment 6 by inane posted on 2/11/2016 at 2:49 PM

I am having a problem related with this topic, running on ios I have this error in the console...

Refused to execute inline script because it violates the following Content Security Policy directive: "default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'". Note that 'script-src' was not explicitly set, so 'default-src' is used as a fallback.

Any clue about it?

Comment 7 (In reply to #6) by Raymond Camden posted on 2/11/2016 at 2:57 PM

You would need to add unsafe-inline.

Comment 8 (In reply to #7) by inane posted on 2/11/2016 at 2:59 PM

my meta-tag line is : " <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *"> " is not correct?

Thank you for your soon response...

Comment 9 (In reply to #8) by Raymond Camden posted on 2/11/2016 at 3:05 PM

unsafe-inline needs to be in default-src or in a new script-src block

Comment 10 (In reply to #9) by inane posted on 2/11/2016 at 3:17 PM

now my meta-tag is: <meta http-equiv="Content-Security-Policy" content="default-src 'unsafe-inline' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; script-src 'unsafe-inline'; media-src *">

and the error: Refused to load the script 'file:///var/mobile/Containers/Bundle/Application/921B2EDB-53C4-485E-B3F6-9F66585804CF/Survey.app/www/cordova.js' because it violates the following Content Security Policy directive: "script-src 'unsafe-inline'".

:-(

Comment 11 (In reply to #10) by Raymond Camden posted on 2/12/2016 at 2:03 AM

Try adding 'self' to script-src too.

Comment 12 by Clash posted on 4/17/2016 at 12:04 PM

<meta http-equiv="Content-Security-Policy" content="

default-src 'self' data: gap: https://ssl.gstatic.com ‘unsafe-eval’;

style-src 'self' 'unsafe-inline';

script-src 'self' 'unsafe-inline';

connect-src https://enigmatic-springs-6...;

media-src *"/>

Comment 13 (In reply to #12) by Clash posted on 4/17/2016 at 12:05 PM

The code above does not work. I wonder why?

Comment 14 (In reply to #13) by Raymond Camden posted on 4/18/2016 at 11:39 AM

How does it not work? What error or unexpected behavior do you get?

Comment 15 by Anup Nair posted on 5/22/2016 at 8:57 AM

<meta http-equiv="Content-Security-Policy" content="default-src 'self' *.xyz.com data: gap: https://ssl.gstatic.com *.xyz.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *;connect-src *">
error message that I am getting in visual studio when running in device is : FAILED TO LOAD RESOURE XYZ.COM but the app works fine on ripple browser.
is the issue related to csp? if so how may I fix it?

Comment 16 (In reply to #15) by Raymond Camden posted on 5/23/2016 at 12:31 AM

How are you trying to load stuff?

Comment 17 (In reply to #16) by Anup Nair posted on 5/23/2016 at 5:45 AM

i'am using $.ajax method to post data to xyz.com/something.php page.its working well while running with ripple in chrome browser but not on my android device

Comment 18 (In reply to #17) by Raymond Camden posted on 5/23/2016 at 11:48 AM

In connect-src, try adding http://xyz.com

Comment 19 by Andrés Zsögön posted on 8/14/2016 at 10:34 PM

The meta tag I was using on WP8 platform (Cordova) does not work anymore for the Windows platform (appx for WP8), all onclick events are ignored. This is the tag that worked: <meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline' 'unsafe-eval' * data:; style-src 'self' 'unsafe-inline'; media-src *; connect-src *"/>
Any ideas what could be the problem? Thanks a lot.

Comment 20 by Shubair posted on 8/14/2016 at 10:45 PM

Hi there, the setting (connect-src) mentioned in the above nice tutorial worked fine with me in both emulator and the Android device when I read JSONP data remotely hosted on the cloud. With the same setting, I tried to run the function below to run a php file, it worked fine on the Ripple - Nexus (Galaxy) but failed on the Android device.
Do I need more setting to make the php file run on the Android device?

function SetRec(sid, grp, typ) {
var postData = $(this).serialize();
var str = "?pid=dummy + "&sid=" + sid + "&grp=" + grp + "&typ=" + typ;
$.ajax({
type: 'POST',
data: postData,
url: 'http://www.mywebdomain.com/... + str,
success: function (data) {
console.log("done.. :-)");
},
error: function () {
console.log("error ..!");
}
});
}

Comment 21 by Shubair posted on 8/15/2016 at 9:04 PM

Solved :-)
By using cordova.InAppBrowser.open

Comment 22 (In reply to #21) by Raymond Camden posted on 8/16/2016 at 3:33 PM

Thanks for sharing your fix!

Comment 23 by Durga Prasad posted on 8/27/2016 at 11:48 AM

I am getting this error with cordova 6.2.0.Can anyone please help

TypeError: undefined is not an object (evaluating 'a.event.props.concat')
(anonymous function) — jquery.mobile-1.4.5.min.js:1104:294
(anonymous function) — jquery.mobile-1.4.5.min.js:1121
(anonymous function) — jquery.mobile-1.4.5.min.js:6
global code — jquery.mobile-1.4.5.min.js:7

Thanks.

Comment 24 (In reply to #23) by Raymond Camden posted on 8/27/2016 at 1:27 PM

This question is not on topic for this blog post. Please post this to StackOverflow and use the Cordova tag.

Comment 25 by Sandra posted on 5/11/2020 at 9:47 AM

Hey, finding this years later - thanks for an awesome write up!

I spotted a formatting issue - you probably want some css like code { white-space: pre; } to make sure the split lines in the "separated by semi colons" code block display as intended.

Enjoy the summer (:

Comment 26 (In reply to #25) by Raymond Camden posted on 5/11/2020 at 1:48 PM

I've changed code formatters a few times over the life time of the blog. I'll see about updating this post.