Comment 3 for bug 457466

Revision history for this message
e.fryntov (e-fryntov) wrote : Re: Site compatibility - investigate white-listing Javascript for certain sites

function werapping approach report:

Javascript "whitelisting" in psiphon

Goal

Perform URL rewriting in javascript methods that make HTTP requests of their own(javascript:open, XMLHTTPRequet.open, etc.)

Method

Overriding native Javascript methods with our own versions that perform URL rewriting prior to making HTTP request.

Theory

It is impossible to recognize wrapped or obfuscated Javascript methods and arguments without parsing the code with a
Javascript engine. Since writing a good Javascript engine and performing parsing of all event driven methods found in the
HTML document is out of scope of this project we will try to make the client browser's JS engine do the job.

Consider the following piece.

Code:

 <script>

/*This method will be overridden*/
function foo(yourname)
{
    alert('Hello ' + yourname);
}

/*New method that overrides foo()*/
foo = function(yourname)
{
    alert('(overide) Hello ' + yourname);
}
</script>

Next time we call foo('John') in our code we'll see "(override) Hello John" popup.
Now, we need to keep a reference to the original method somehow, because we do not want to override the method completely, just add some URL
rewriting routine.

Code:

 <script>

/*This method will be overridden*/
function foo(url)
{
    alert('The URL is '+url);
}

/*keeping the reference to the original foo as base_foo*/
var base_foo = foo;

/*New method that overrides foo()*/
foo = function(url)
{
    var new_url = psiphon_rewrite(url);
    base_foo(url);
}
</script>

Practice

We've tried this approach with the following websites:

www.youtube.com(upload a video functionality)
it uses a lot of AJAX techniques so it was a good candidate. The XMLHTTPRequest(XHR) was wrapped using
ideas from XMLHTTPRequest.js project. We were able to upload files but the page would show 'Unknown upload errors'.

ozodlik.org(general JS functionality)
We couldn't get search button to work.

Problems analysis

In case of youtube.com some XHRs return responses that are used to dynamically change InnerHTML or other properties of
DOM elements. Sometimes these properties are URLs(divs dynamically changed to imgs).

Further debugging showed dynamic attachment of
JS events to DOM elements via XHR, such as "onload". Combination of the two produced the error described above as the dynamic image couldn't load causing
"onload" event to never fire.

Code:

<script>
.............
      xmlHttp.onreadystatechange=function()
     {
         if(xmlHttp.readyState==4)
         {
            var imgholder = document.getElementById("imgholder");
            var img = document.createElement('img');
            img.onload = function (e) {
                dosomething_onload();
            }
            img.onerror = function(e) {
                report_error(e);
            }
            img.src = xmlHttp.responseText;
            imgholder.appendChild(img);
        }
      }
.........
 </script>

The ozodlik.org showed a different kind of problem which is a 'document.location' object. In theory location.href is a property of location object, but changing it
causes document to reload with the new URL.

Code:

<input id="keywords0_" name="keywords0_" onkeypress="if (event.keyCode == 13) {location.href ='/search/?k=' + encodeURI(this.value);return false;}"...

As we can see there is no method here so it is not clear how to intercept the URL in such a construct if possible at all.

Another potential problem is to recognize and prevent re-wrapping overridden methods by the document itself as it may use something like XMLHTTPRequest.js to wrap
native XMLHTTPRequest for example.

Code:

<script>

/*This method will be overridden*/
function foo(url)
{
    alert('The URL is '+url);
}

/*keeping the reference to the original foo as base_foo*/
var base_foo = foo;

/*New method that overrides foo()*/
foo = function(url)
{
    var new_url = psiphon_rewrite(url);
    base_foo(url);
}

/*Yet another method that overrides foo()*/
foo = function(url)
{
    dosomething_else();
}
</script>

Conclusion: this approach is a possible but far from full solution in javascript white-listing.