Location hack: Difference between revisions

From GreaseSpot Wiki
Jump to navigationJump to search
(please provide more detail in the talk page about when/how/why this happens, I cannot confirm it, nor that it would break anything if it does happen.)
(remove obsolete content)
 
(45 intermediate revisions by 7 users not shown)
Line 1: Line 1:
The '''location hack''' is an ugly but useful way to interact with the content scope of the page being [[user script]]ed. It does this by indirectly [http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Functions:eval evaling] strings within that scope.
The location hack is an older technique for crossing the barrier between the user script scope and the content window scope, when it exists.
The previous version of this page may be read [http://wiki.greasespot.net/index.php?title=Location_hack&oldid=7202 via history].


== Background ==
In Firefox version 39.0.3 Mozilla patched a known [https://www.mozilla.org/en-US/security/advisories/mfsa2015-78/ security vulnerability].
As a side effect, this broke the location hack.


For [[security]] reasons, [[Greasemonkey]] uses [[XPCNativeWrapper]]s and [[sandbox]] to isolate it from the web page. Under this system, the user script can access and manipulate the page using event listeners, the DOM API, and [[API reference|GM_* functions]].
Thankfully in modern versions of Firefox and Greasemonkey, the location hack is no longer necessary.
 
Read about how to execute code in the content scope, like previously possible with the location hack, at [[Content Script Injection]].
Sometimes the sandbox is too limiting, in which case the user script can access other parts of the page using [[unsafeWindow]]. As the name ''unsafe''Window implies, this can often be unsafe, and expose security holes.
 
In December 2005, Jesse Ruderman [http://www.squarefree.com/2005/12/01/updated-greasemonkey-scripts/ came up with the location hack], to be an alternative to unsafeWindow in many cases.
 
== Basic usage: page functions ==
 
Suppose the page contains a function called <code>pageFunc</code>, or <code>window.pageFunc</code>.
The user script knows this function as <code>unsafeWindow.pageFunc</code>.
 
The user script could simply call <code>unsafeWindow.pageFunc()</code>, but this can leak the sandbox.
Instead, the user script can take advantage of <code>javascript:</code> URLs, which always run in the content scope.
Just entering this URL into the browser's location bar does not leak a GreaseMonkey sandbox:
 
javascript:pageFunc();void(0)
 
A user script can programmaticaly navigate to this URL, to safely call the function:
 
location.assign( "javascript:pageFunc();void(0)" );
 
That, in a nutshell, is the location hack!
Essentially, it is wrapping a [[wikipedia:bookmarklet|bookmarklet]] into a user script.
 
It's important to add the <code>javascript:</code> scheme to the front, to turn it into a URL, and the <code>;void(0)</code> to the end, which keeps the browser from actually navigating to this URL after it is run.
 
== Modifying the page ==
 
The location hack can do anything a page script or bookmarklet can do, so it can modify content variables and such as easily as it can access them. For example:
 
location.href = "javascript:void(window.someVariable = 'someValue')";
 
== Executing large blocks of code ==
 
Executing more than one statement can become unreadable very easily. Luckily, JavaScript can convert functions to strings, so you can use:
 
location.href = "javascript:(" + function() {
  // do something
} + ")()";
 
Even though the function is defined in the sandbox, it is not a closure of the sandbox scope. It is converted to a string and then back to a function in page scope. It cannot access anything in the sandbox scope, which is a limitation, but is also essential to making this technique secure.
 
== Percent encoding issue ==
 
Sometimes percent-encoding the percent symbol is required. For example,
 
location.href = ("javascript:(" + function() {
  var n = 44;
  if(!('''n%22''')) alert('n is a multiple of 22');
} + ")()");
 
The above code will cause error because %22 is interpreted as double quotation mark. The workaround is:
 
location.href = "javascript:(" + encodeURI(
  function() {
  var n = 44;
  if(!(n%22)) alert('n is a multiple of 22');
  }) + ")()";
 
See also [http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Functions:encodeURI encodeURI()].
 
== Returning values ==
 
Functions called through the location hack cannot return data directly to the user script scope. To communicate between location hack code and regular user script code, data must be placed where the user script can see it, for example, by writing it into the DOM, or by triggering an event. A simple example:
 
var oldBodyTitle = document.body.title;
location.href = "javascript:void(document.body.title = pageFunc())";
var fauxReturnValue = document.body.title;
document.body.title = oldBodyTitle;
 
This trick, however, can be defeated by the hosting page by simply overriding the <code>document.body.title</code> property with a [http://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Creating_New_Objects/Defining_Getters_and_Setters setter].
 
document.body.__defineSetter__("title", function(t){} );
 
While this does not mean a threat to the user visiting the page, it effectively prevents the userscript from accessing the returned value of the global function.  A working example is available [http://esquifit.googlepages.com/LocationHackDefeat.html here].
 
However, __defineSetter__ itself can be overridden using the [http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Operators/Special_Operators/delete_Operator delete operator].  A modified version of the linked user script above, using delete to ignore the setter, is [[Defeat_setter_example|available]].
 
=== Function to get values of global variables ===
 
The following function can be used to access the values of global variables using <code>var value = GM_getGlobalElement('globalVariable');</code>. Please note that the returned value is converted to a string.
 
// function to get values of global variables using the "location hack"
window.GM_getGlobalElement = null;
window.getGlobalValue = function(name) {
  if (GM_getGlobalElement == null) {
    GM_getGlobalElement = document.createElement("textarea");
    GM_getGlobalElement.id = "GM_getGlobalElement";
    GM_getGlobalElement.style.visibility = "hidden";
    GM_getGlobalElement.style.display = "none";
    document.body.appendChild(GM_getGlobalElement);
  }
  location.href = "javascript:void(typeof("+ name + ")!=\"undefined\"?document.getElementById(\"GM_getGlobalElement\").value=" + name + ":null)";
  return(GM_getGlobalElement.value);
}
 
== See Also ==
 
* [[Location Hack Helper]]
* [[Content Script Injection]]


[[Category:Coding Tips:Interacting With The Page]]
[[Category:Coding Tips:Interacting With The Page]]

Latest revision as of 15:54, 23 September 2015

The location hack is an older technique for crossing the barrier between the user script scope and the content window scope, when it exists. The previous version of this page may be read via history.

In Firefox version 39.0.3 Mozilla patched a known security vulnerability. As a side effect, this broke the location hack.

Thankfully in modern versions of Firefox and Greasemonkey, the location hack is no longer necessary. Read about how to execute code in the content scope, like previously possible with the location hack, at Content Script Injection.