I'm aware that there is a Cross site forgery attack that can be performed on a request that returns an array by overloading the Array constructor. For example, suppose I have a site with a URL:
foo.com/getJson
that returns:
['Puff the Dragon', 'Credit Card #']
This would normally be Javascript eval
'd by my own site after an XHR request, but another site can sniff this data by including something like:
<script>
function Array() {
var arr = this;
var i = 0;
var next = function(val) {
arr[i++] setter = next;
document.write(val);
};
this[i++] setter = next;
}
</script>
<script src="http://foo.com/getJson"></script>
My question is, can the same thing be done when the request returns a Javascript object? i.e.
{ name: 'Puff the Dragon', cc: 'Credit Card #' }
I couldn't figure out a way to do this, but maybe I'm missing something. I know there are better solutions to protect my site, like using the while(1) hack or requiring an auth token in the URL, but I'm trying to figure out if this sort of security hole exists.
The sources I've seen, such as Haacked and Hackademix, specifically indicate that root objects are safe (presumably in all major browsers). This is because a script can not start with an object literal. By default, ASP.NET wraps both objects and arrays with a d prefix, but I think this is just to simplify the client library.
It looks like from the Ecmascript spec, the JSON object shouldn't be treated as a valid Javascript program:
"Note that an ExpressionStatement
cannot start with an opening curly
brace because that might make it
ambiguous with a Block.
So assuming that all browser implement this correctly, a response like { name: 'Puff the Dragon', cc: 'Credit Card #' }
won't be executed as valid Javascript. However expressions like ({name: 'Puff the Dragon', cc: 'Credit Card #' })
and {['Puff the Dragon', 'Credit Card #']}
will.
You could use the same technique for Object
. It wouldn't affect the prototype chain, so it wouldn't be inherited by all objects. But you could, for example, log all new objects getting created with this:
function Object() {
var obj = this;
if (window.objectarray === undefined) {
window.objectarray = [];
}
window.objectarray.push(this);
return this;
}
Any time code on your page uses new Object()
, it would get written to window.objectarray
-- even if it were created in a private scope. So, for example, look at this code:
var Account = function() {
var createToken = function() {
var objToken = new Object();
objToken.timestamp = new Date().getTime();
objToken.securestring = "abc123";
return objToken.timestamp + objToken.securestring;
}
var objPrivate = new Object();
objPrivate.bankaccount="123-456789";
objPrivate.token = createToken();
};
var myAccount = new Account();
In this case, if you create a new account with new Account()
, a token will be created using private properties (and maybe methods) and nothing about myAccount
is left hanging outside in public. But both 'objectToken' and objPrivate
will be logged to window.objectarray
.