Use appendChild correctly to avoid XSS - javascript

After doing an XXS site check, one error that pops up in the console is the inline use of the scripts needed for Google Analytics Tag Manager.
I have now created an external analytics.js file, which I load in the header and created an IIFE to load the script, then checked that it works in GA (it does):
var load_google_tag_manager = function(){
var script = document.createElement("script");
var head = document.getElementsByTagName('head')[0];
script.async = true;
script.type = "text/javascript";
script.src = "https://www.googletagmanager.com/gtag/js?id=UA-XXXXXX-1";
head.appendChild(script);
}();
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'UA-XXXXXX-1', {
"anonymize_ip": true,
"allow_display_features": false,
"link_attribution": false
});
I then checked the console again for error warnings and got one, this was the use of the function appendChild.
After reading some OWASP documentation, I understand that using this can be potentially dangerous, and some good examples of how to make it safer are given, such as: how to use the setAttribute method. e.g.:
SAFE and FUNCTIONALLY CORRECT example
var x = document.createElement("input");
x.setAttribute("name", "company_name");
x.setAttribute("value", '<%=Encoder.encodeForJS(companyName)%>');
var form1 = document.forms[0];
form1.appendChild(x);
But it does not say how to do this when you are e.g not using this method.
How would I e.g. set the src value safely?
Ultimately, I want to make my IIFE safe from XXS.

The encoding is needed in the example because of the mixing of server-side templating with client-side code. The quotes need to be escaped to prevent the string literal from being closed, and more code added after it.
So if companyName were '); alert(1);//, then:
x.setAttribute("value", '<%=companyName%>');
would become:
x.setAttribute("value", ''); alert(1);//');
Your URL looks like a fixed constant, so there is no possibility of something like this, so your code should be fine as it is.

Related

Chrome console behaviour

I am injecting through console jquery:
var jq = document.createElement('script');
jq.src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js";
document.getElementsByTagName('head')[0].appendChild(jq);
jQuery.noConflict();
Then I am using some jquery command
$('.first').position()
document.elementFromPoint(xPosition, yPosition).click();
After emulating click page in browser reloading. And than $('.first') allways return []; But on the page there are a lot of tags with class 'first'. It seems that console waiting for updating? Or what?
The appendChild is loading a script element into the DOM, that script tag then goes and downloads the file set to its src property. But this download takes time. It's asynchronous.
var jq = document.createElement('script');
jq.src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js";
document.getElementsByTagName('head')[0].appendChild(jq);
So when you attempt to access jQuery immediately after in your javascript, that file hasn't been downloaded yet. Imagine that the file took an hour to download. You'd need to wait to be notified that the file had finished downloading before using that code. There's a number of ways to think about this, like using an interval to check if the global variable is there, but I think the simplest is just to use an onload event.
var jq = document.createElement('script');
jq.src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js";
jq.onload = function(){
//do stuff using jquery
}
document.getElementsByTagName('head')[0].appendChild(jq);

scripts required by kissmetrics

KISSMetrics sometimes causes trouble when loading on my site. Those familiar with it will probably know about the following snippet which appears in their docs:
<!-- Kissmetrics tracking snippet -->
<script type="text/javascript">var _kmq = _kmq || [];
var _kmk = _kmk || '61d817358af517ab2975dbb768eeb1d6d0d07c4b';
function _kms(u){
setTimeout(function(){
var d = document, f = d.getElementsByTagName('script')[0],
s = d.createElement('script');
s.type = 'text/javascript'; s.async = true; s.src = u;
f.parentNode.insertBefore(s, f);
}, 1);
}
_kms('//i.kissmetrics.com/i.js');
_kms('//scripts.kissmetrics.com/' + _kmk + '.2.js');
</script>
cf.: https://app.kissmetrics.com/setup/integration/9ac2445636036f9151b84b444b1ae78d105d0f7a
This is really fancy and I want to know why I can't do it more simply than this.
Opening the file i.kissmetrics.com/i.js we see the one-liner:
if(typeof(_kmil) == 'function')_kmil();
which means 'run _kmil() if you recognize _kmil as a function.' Meanwhile, _kmil() is an alias of KM.ikmq(), a function defined in the second script that you see.
It seems that the trouble appears when that second script, scripts.kissmetrics.com/61d817358af517ab2975dbb768eeb1d6d0d07c4b.2.js° either fails to load or has a delay in loading.
I want to do the following instead:
Put scripts.kissmetrics... into the head node of my html, and then, rather than include i.js, simply run the function window.KM.ikmq() just before I register my listeners for KISSMetrics-tracked click events.
What would be the drawbacks of this, if any?
Totally optional bonus question: what is the purpose of the first line of code in the snippet above var _kmq = _kmq || []; if the variable _kmq is not used in the remainder of the script?
° the hexadecimal string is a fake key used here for demonstration purposes

Executing script inserted with innerhtml

I'm using the following code to attempt to execute one of adNetworks banners after an innerhtml has been called
// container is where you want the ad to be inserted
var container = document.getElementById('sponsor_container2');
var w = document.write;
document.write = function (content) {
container.innerHTML = content;
document.write = w;
};
var script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = 'http://go.adversal.com/ttj?id=2609728&size=728x90&promo_sizes=468x60,320x50,300x50,216x36';
document.body.appendChild(script);
However for whatever reason I reason the following message
Resource interpreted as Script but transferred with MIME type text/html: "http://ib.adnxs.com/ttj?id=2609728&size=728x90&promo_sizes=468x60,320x50,300x50,216x36".
And nothing happens, the above code has worked in the past for previous adNetworks so this is why I'm so dumb founded
This is the script my ad page provided me
<script src="http://go.adversal.com/ttj?id=2609728&size=728x90&promo_sizes=468x60,320x50,300x50,216x36" type="text/javascript"></script>
I think your problem is the fact that you have re-defined a function that is already in the Javascript document notation: document.write() is a function that is used to write to the document, and as you may know, you cannot re-define defined functions in Javascript.
You probably just want document.write(w) or something like that.
Hope I could help.

Can't insert js programmatically if it uses document.write

I am trying to insert js files programmatically, using jquery and something like this:
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.src = 'http://someurl/test.js';
$('body').append(script);
It works fine, if test.js contains an alert or some simple code it works fine, but if the file test.js contains document.write, and the file including the js is hosted on another domain than test.js (or localhost), nothing happens and firebug shows the error :
A call to document.write() from an asynchronously-loaded external
script was ignored.
If the test.js and the file that include it are hosted on the same domain, on chrome it still wont work but on firefox the document.write gets executed fine but the page stays "loading" forever and sniffer show request to all the files with "pending" status.
What other methods to include js files programmatically could I try?
use innerHTML instead of using document,write.
and use following code to register script,
(function() {
var jq = document.createElement('script');
jq.type = 'text/javascript';
jq.async = true;
jq.src = 'http://someurl/test.js';
var s = document.body.getElementsByTagName('script')[0];
s.parentNode.insertBefore(jq, s);
})();
Document.write is ONLY for synchronous tasks when the html is loaded (for the very first time), never for asynchronous tasks like the one you are trying to do.
What you want to do is dynamically insert a <script> DOM element into the HEAD element. I had this script sitting around. As an example, it's a race condition, but you get the idea. Call load_js with the URL. This is done for many modern APIs, and it's your best friend for cross-domain JavaScript.
<html>
<head>
<script>
var load_js = function(data, callback)
{
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.type = "text/javascript";
script.src = data;
head.appendChild(script);
if(callback != undefined)
callback();
}
load_js("http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js");
setTimeout(function() {
$('body').html('loaded');
}, 1000);
</script>
</head>
<body></body>
</html>
There isn't anything wrong with your approach to inserting JavaScript. document.write just sucks a little bit. It is only for synchronous tasks, so putting a document.write in a separate script file is asking for trouble. People do it anyway. The solution I've seen most often for this is to override document.write.

How to include jquery.js in another js file?

I want to include jquery.js in myjs.js file. I wrote the code below for this.
var theNewScript=document.createElement("script");
theNewScript.type="text/javascript";
theNewScript.src="http://example.com/jquery.js";
document.getElementsByTagName("head")[0].appendChild(theNewScript);
$.get(myfile.php);
There shows an error on the 5th line that is '$ not defined'. I want to include jquery.js and then want to call $.get() function in myjs.js file. How can I do this?
Please help me
Appending a script tag inside the document head programmatically does not necessarily mean that the script will be available immediately. You should wait for the browser to download that file, parse and execute it. Some browsers fire an onload event for scripts in which you can hookup your logic. But this is not a cross-browser solution. I would rather "poll" for a specific symbol to become available, like this:
var theNewScript = document.createElement("script");
theNewScript.type = "text/javascript";
theNewScript.src = "http://example.com/jquery.js";
document.getElementsByTagName("head")[0].appendChild(theNewScript);
// jQuery MAY OR MAY NOT be loaded at this stage
var waitForLoad = function () {
if (typeof jQuery != "undefined") {
$.get("myfile.php");
} else {
window.setTimeout(waitForLoad, 1000);
}
};
window.setTimeout(waitForLoad, 1000);
The problem is that the script doesn't load instantly, it takes some time for the script file to download into your page and execute (in case of jQuery to define $).
I would recommend you to use HeadJS. then you can do:
head.js("/path/to/jQuery.js", function() {
$.get('myfile.php');
});
Simple answer, Dont. The jQuery file
is very touchy to intruders so dont
try. Joining other files into jQuery
file will often cause errors in the JS
console, PLUS jQuery isn't initialized
until the file is loaded into main
document.
Sorry, scratch that. Didnt quite know what you were doing.
Try this:
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'http://domain.com/jquery.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(s);
I used this code before, and it worked:
var t=document;
var o=t.createElement('script');
o=t.standardCreateElement('script');
o.setAttribute('type','text/javascript');
o.setAttribute('src','http://www.example.com/js/jquery-1.3.2.js');
t.lastChild.firstChild.appendChild(o);

Categories

Resources