Igor Kromin |   Consultant. Coder. Blogger. Tinkerer. Gamer.

Recently I've become interested in whether it wcould be possible to hide the Google Analytics or Google AdSense publisher IDs from online tools like SpyOnWeb.com and any lay people who may be looking at your site's code. This is when I came across this article that talked about de-obfuscating JavaScript encoded with JJencodep. After reading that article I had a few ideas and over a number of iterations came up with something that fit the bill.

First a bit of a disclaimer though - this approach is not 100% guaranteed to hide the publisher IDs, it's only meant to make it harder for the casual onlooker to see what the ID is. For someone determined this would be possible to bypass with a some effort, after all it is all client-side code.

In case you're wondering why you'd want to hide your publisher ID - well that's simply if you reuse the same ID across all of your web sites, it makes it easy for anyone to track down all of your sites and that's not always desirable.
obfs_js.png


So lets get started. I will show the easy case here, which is the Google Analytics (GA) ID, but the same process can be adapted to AdSense. A typical GA ID looks like this: UA-12345678-1.

Somewhere in the GA code there is a line that looks like this...
 JavaScript
_gaq.push(['_setAccount', 'UA-12345678-1']);


The idea is to hide the GA ID and make it not appear directly in the code. Looking at the format of the ID and assuming it doesn't vary we can take a shortcut and encode just the numerical portion of it. This can be done by creating an object with properties that store numbers between 0 and 9. Then that object is used to concatenate the numbers into a string representing the GA ID. Since JavaScript is very flexible with variable names we already have something that's hard to read in the code below.
 JavaScript - First Version
/* object with number values */
_={
$: 0,
_: 1,
$_: 2,
$$: 3,
__: 4,
_$: 5,
$$$: 6,
$$_: 7,
$__: 8,
___: 9
}
/* UA-12345678-1 */
var gaId = 'UA-' + _._ + _.$_ + _.$$ + _.__ + _._$ + _.$$$ + _.$$_ + _.$__ + _.___ + '-' + _._;


However it is still very easy to see what that code is doing. Furthermore that code defines a global variable that is readily accessible after the code is long done executing so you can access it any time again and again.



The next version is a little bit more complex. It takes some ideas from the deobfuscation article I linked at the top and creates the number value object using some basic variable incrementation. The initial value of the _ variable is set to []&[] which evaluates to 0, then each of the properties of the value object build on top of this by incrementing the value (after the zero value is used). In order to hide the number value object it is declared a local variable inside the $() function.
 JavaScript - Second Version
/* object with a function returning the GA ID */
var Z={
$: function() {
var _ = []&[];
_ = {
$: _,
_: ++_,
$_: ++_,
$$: ++_,
__: ++_,
_$: ++_,
$$$: ++_,
$$_: ++_,
$__: ++_,
___: ++_
};
/* UA-12345678-1 */
return 'UA-' + _._ + _.$_ + _.$$ + _.__ + _._$ + _.$$$ + _.$$_ + _.$__ + _.___ + '-' + _._;
}
};
var gaId = Z.$();


Logically the second version is equivalent to the first, with some object layers introduced. It makes it a little more difficult to work out the hidden value, but is still far from being good at achieving the goal. There are a couple of problems - first the string 'UA-' is clearly visible which is a dead giveaway. Second the Z object is accessible after the call to Z.$() so it's possible to retrieve the hidden value at any time and as many times as you wish just like in the first version.

Behold the third version to address these shortcomings...
 JavaScript - Third Version
/* object with a function returning the GA ID */
var Z={
$: function() {
try {
var _=[]&[];
_={
$: _,
_: ++_,
$_: ++_,
$$: ++_,
__: ++_,
_$: ++_,
$$$: ++_,
$$_: ++_,
$__: ++_,
___: ++_,
__$: '\144\x65\143\157\144\145\x55\122\111',
_$_: '\x25',
_$$: '\055'
};
return window[_.__$](
/* U */
_._$_ + _._$ + _._$ +
/* A */
_._$_ + _.__ + _._ +
/* - */
_._$$ +
/* 12345678 */
_._ + _.$_ + _.$$ + _.__ + _._$ + _.$$$ + _.$$_ + _.$__ + _.___ +
/* - */
_._$$ +
/* 1 */
_._
);
}
finally {
Z = []|[];
}
}
};
var gaId = Z.$();


Now that's a lot harder to decipher. So lets see what it does. The number value object has a few additional properties added to it:
  • '\144\x65\143\157\144\145\x55\122\111' - this is a string evaluating to 'decodeURI', which is a function used to decode %-encoded strings. Note that I used a mix of octal and hexadecimal character representations here just for a bit of extra obscurity
  • '\x25' - a string evaluating to the % character
  • '\055' - a string evaluating to the - character


Values for the above character escape sequences can be looked up at www.asciitable.com.

The window[_.__$](); part of the code is used to call the decodeURI function, the input parameter to it is a %-encoded string. The very first part of that string encodes the letter 'U' - which is represented by _._$_ + _._$ + _._$ in the code. That concatenation evaluates to '%55' which is a hexadecimal code for 'U'. The rest of the GA ID string is built up in a similar fashion, using a mix of %-encoding and simple concatenation.

You will also notice that all the code in the $() function is wrapped in one try/finally block. The finally clause sets Z to []|[] which evaluates to 0. This is used to 'self destruct' the Z object after the $() function is called on it. The overall effect is to return the hidden GA ID the very first time it is requested, but any further attempts to retrieve it will result in an error like below.
 Error
TypeError: Z.$ is not a function. (In 'Z.$()', 'Z.$' is undefined)


So now all the formatting and white space can be removed to give this final version...
 JavaScript - Final Version
var Z={$:function(){try{var _=[]&[];_={$:_,_:++_,$_:++_,$$:++_,__:++_,_$:++_,$$$:++_,$$_:++_,$__:++_,___:++_,__$:'\144\x65\143\157\144\145\x55\122\111',_$_:'\x25',_$$:'\055'};return window[_.__$](_._$_+_._$+_._$+_._$_+_.__+_._+_._$$+_.$$+_.___+_._+_.$+_.__+_._+_.__+_.___+_._$$+_._);}finally{Z=[]|[];}}};


That one is definitely hard to read or make sense of. The above can be put into a separate JavaScript file and loaded before the GA code runs. Then to get it working with the GA code you'd do something like...
 JavaScript
_gaq.push(['_setAccount', Z.$()]);


When I get some spare time I'll put together a tool to create these kind of encoded/hidden IDs and will extend it to AdSense too, but for now it remains an exercise for the reader to encode the value manually.

-i

A quick disclaimer...

Although I put in a great effort into researching all the topics I cover, mistakes can happen. Use of any information from my blog posts should be at own risk and I do not hold any liability towards any information misuse or damages caused by following any of my posts.

All content and opinions expressed on this Blog are my own and do not represent the opinions of my employer (Oracle). Use of any information contained in this blog post/article is subject to this disclaimer.
Hi! You can search my blog here ⤵
NOTE: (2022) This Blog is no longer maintained and I will not be answering any emails or comments.

I am now focusing on Atari Gamer.