Безопасность. Модификация кода ядра платформы подгружаемыми апплетами

    Все что описано ниже касается только клиентской части реализованной на JavaScript. Приветствуется критика технического характера (например, пути обхода), но не критка в стиле «нафига это нужно».

    При разработке модуля подключения апплетов к некой платформе (назовем ее Hyper) появилась задача обеспечения безопасности, так как подключаемый апплет кроме пассивного блока данных (content) содержит и активный (code). А в активном блоке невнимательный разработчик (злоумышленник) может обратиться к глобальному объекту window и получить доступ к переменным или важным методам ядра и сделать подмену, что в лучшем случае просто приведет к краху платформы (в пределах браузера конечно), а в худшем установка различных хуков не влияющих на работу системы, но перехватывающих персональные данные пользователя. В дальнейшем ничего не подозревающий пользователь будет пользоваться гаджетом «Часы от Боба», который по тихому собирает о нем информацию, или рассылает спам по адресам из контакт листа.

    Уже была рассмотрена проблема коллизий глобальных переменных и предложено решение об изоляции подгружаемого кода от кода ядра платформы. Всем известно что при создании переменной в глобальном пространстве имен она становится свойством объекта window. Например var a = 10; одно и тоже что window.a = 10;

    Наша задача изолировать код ядра чтобы сторонние скрипты не смогли получить к нему доступ. Изоляция базируется на создании анонимной функции и моментального вызова этой функции для инициализации локальных переменных и объектов.
    Пример: (function () { a = 10; })(); Функция анонимна и не будет видна через объект window.

    Заведем простенькое ядро:

    var Core = {};
    Core.sName = «MyCore»;
    Core.MyMethod = function(sMyValue) { alert(sMyValue); };
    * This source code was highlighted with Source Code Highlighter.

    Изолируем ядро:

    (function () {
     var Core = {};
     Core.sName = «MyCore»;
     Core.MyMethod = function(sMyValue) { alert(sMyValue); };
    })();
    * This source code was highlighted with Source Code Highlighter.


    Теперь создадим функцию загрузки апплета:

    Applet.Link = function(sCode, oContext) { (new Function(sCode)).call(oContext); }, где

    sCode — исполняемый код апплета, oContext — важный параметр. Если взглянуть шире то это понятие контекста из мира desktop программного обеспечения. Кто писал на ассемблере под protected mode поймут о чем речь. Вкратце контекст содержит информацию о состоянии системы. В desktop приложениях там сохраняется информация о регистрах, т.е. само приложение может их менять и при этом не нарушая работу всей системы. В нашем случае контекст содержит переменные и объекты платформы необходимые для работы апплета. Наша задача при запуске апплета на исполнение сформировать контекст. Для этого нам необходимо знать какие переменные и методы платформы нужны коду апплета. Будем рассматривать уже созданные объекты. Допустим апплету нужен метод MyMethod из пространства имен Core.
    Создание контекста будем делать вручную (в Hyper это все автоматизировано, есть описатель апплета в xml формате и понятие ImportBlock, где перечисляются импортируемые переменные и объекты). В статье для усвоения материала все упрощено. Итак получаем контекст: var oContext = { MyMethod: Core.MyMethod) }. Обьединим это все в коде:

    (function() {

     // Core module.
     var Core = {};
     Core.sName = «MyCore»;
     Core.MyClass = function() {};
     Core.MyClass.prototype.MyMethod = function(sMyValue) { alert(sMyValue); };

     // Applet module.
     Applet = {};
     Applet.Link = function(sCode, oContext) { (new Function(sCode)).call(oContext); }

     // Load applet module.
     var sCode = «this.MyMethod('Hello world');»;
     var oContext = { MyMethod: Core.MyMethod };
     Applet.Link(sCode, oContext);

    })();
    * This source code was highlighted with Source Code Highlighter.

    Что происходит в полученном коде? Cоздается апплет с кодом: this.MyMethod('Hello world'); Так как функция с данным кодом запускалась в контексте объекта oContext содержащего свойство MyMethod, это свойство становится доступным в теле этой функции через this. Работа апплета выведет алерт с текстом Hello world. Вернемся к злоумышленникам или просто невнимательным разработчикам.
    Был создан апплет #1 с кодом и запущен на исполнение:

    this.MyMethod = function() { alert(«Hack!!!») };
    this.MyMethod('Hello world!!!');
    * This source code was highlighted with Source Code Highlighter.

    В результате этот апплет покажет алерт с текстом Hack. Апплет пытается заменить метод из пространства имен Core, но на самом деле он поменяет метод только в своем контексте. И поэтому другой апплет использующий тот же метод отработает нормально.

    Создан апплет #2 с кодом и запущен на исполение:

    Core.MyMethod = function() { alert('Hack!!!'); };

    Здесь злоумышленник в наглую попытался изменить Core.MyMethod, но его апплет отработает с ошибкой, потому что Core неопределен и мы знаем почему.
    И теперь весь код:

    (function() {

     // Core module.
     var Core = {};
     Core.sName = «MyCore»;
     Core.MyMethod = function(sMyValue) { alert(sMyValue); };

     // Applet module.
     Applet = {};
     Applet.Link = function(sCode, oContext) { (new Function(sCode)).call(oContext); };

     // Load applet module from hacker #1.
     var sCode = «this.MyMethod = function() { alert('Hack!!!'); }; this.MyMethod('Hello world');»;
     var oContext = { MyMethod: Core.MyMethod };
     Applet.Link(sCode, oContext);

     // Load applet module from hacker #2.
     var sCode = «Core.MyMethod = function() { alert('Hack!!!'); };»;
     var oContext = { MyMethod: Core.MyMethod };
     Applet.Link(sCode, oContext);

     // Load applet module from right developer.
     var sCode = «this.MyMethod('Hello world');»;
     var oContext = { MyMethod: Core.MyMethod };
     Applet.Link(sCode, oContext);

    })();
    * This source code was highlighted with Source Code Highlighter.

    ВАЖНО!!! При формировании контекста необходимо следить чтобы туда не попала ссылка на объект, иначе вредоносный апплет сможет подменить весь объект с методами и уже вся платформа будет работать с вредоносным кодом. Проблема решается написанием функции для формирования контекста, которая учитывает все что импортируется и производит копирование по иерархии объекта.

    Вывод: Была рассмотрена вторая проблема безопасности платформы имеющей возможность разработки апплетов под нее. Первая, напомю, это коллизии имен переменных в глобальном пространстве имен. Есть еще третья проблема это доступ к глобальным свойствам браузера (window, document и т.д.), но эта тема для другой статьи.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 53

      0
      под cut засуньте пжлста
        0
        Сорри уже в третий раз забыл. Все сделал.
        0
        А как всеже это решает проблему безопасности? Как я понимаю те же cookies своровать это не мешает, отправить любые данные из dom тоже. Так зачем злоумышленнику вообще заменять код ядра?
          0
          Изменить может не только злоумышленник, но и разработчик по невнимательности. "Есть еще третья проблема это доступ к глобальным свойствам браузера (window, document и т.д.), но эта тема для другой статьи."
          +4
          Счастливый обладатель FF2 пишет что то вроде
          var sCode = "eval('alert(Core.sName);',arguments.callee.caller);";
          и получает доступ к ядру системы :)

          FF2 позволяет выполнять код в произвольном контексте, в данном случае в контексте метода Link для которого ядро доступно.

          Имхо, невозможно создать защищенный код в JavaScripte, проще иметь здоровую параною и не доверять ничему что приходит с клиентской стороны.
            0
            Серьезное замечание. Но чем более он защищен тем меньше вероятность появления вредоносного скрипта. Пока на ум приходит предварительная проверка кода на поиск таких конструкций, например регулярными выражениями.
              +2
              код можно элементарно пошифровать
              когдато решал эту проблему
              самая реальная защита при необходимости запускать чужой javascript это запуск скриптов в разных доменах.
              тоесть, есть main.siteurl.com и runner.siteurl.com
              Страница запускается на субдомене main и в iframe подгружает страницу с runner.
              Скрипты могут обмениваться данными через специальные куки с доменом .siteurl.com
              и только данными, никакого кода естественно. Таким образом вражеские скрипты не могут иметь доступа к основному коду и к кукам пользователя на main.
              0
              eval вообще нужно запретить, время уже играет не в пользу этой функции, от нее отказываются разработчики, второе вопросы безопасности клиентского кода станут острее когда количество платформ для разработки приложений увеличится и они будут занимать значительную часть инета. А eval мое мнение это серьезная брешь в безопасности клиентского кода.
                0
                eval это такой-же метод javascript-a как и другие, никакого вселенского зла в нем нет, и даже если заблокировать сам eval остаются альтернативные варианты ( setTimeout, execScript[IE] )
                В коде статьи используется "new Function" - этот же тот самый eval только вид сбоку.
              0
              Прошу прощения за занудство, но "В кратце" пишется слитно.
                0
                Поправим.
                0
                слово applet тут не совсем пляшет, я как Java программер привык асоциировать applet с Java-applet, долго вьезжал где же тут Java, а вообще еще не мешалобі переопределиь eval, положив "за щеку оригинальную функцию", и в дальнейшем внутри переопределенной проверять что на вход подается.
                  0
                  Да Eval надо контроллировать, проверять на входные параметры и вызывать родной eval уже если все нормально. Например проверять на контекст, если запуск кода производится в контексте функций которые знают о ядре - останавливать запуск.
                    0
                    Тоже не решит всей проблемы. Если оригинальный eval не доступен, можно создать новый ифрейм и воспользоваться eval-ом новосозданного окна.
                      0
                      Ирфейм вообще опасная штука. Для апплетов он вообще не нужен чтобы код разработчика его создавал. По сути проблему как с eval так и другими нативными методами и объектами браузера решается на уровне третьей проблемы о которой уже упоминал в статье. Решение будет базироваться на подмене глобальных объектов и методов в коде апплета.
                  0
                  Хммм.. Это только у меня ошибка в ФФ3 выскакивает?


                    0
                    защита в действии :)
                    Permission denied to get property Element.childNodes
                    • НЛО прилетело и опубликовало эту надпись здесь
                    0
                    Обёртка кода очень полезна для того, чтобы различные скрипты не конфликтовали друг с другом но говорить в данном контексте о безопасности в глобальном смысле какбе некорректно. Если злоумышленник получил доступ к браузеру пользователя, то наличие скриптов, их отсутствие или вот такая обёртка уже ни на что не влияют.
                      0
                      В глобальном смысле безопасности достигнуть нереально. Но всегда можно уменьшить количество лазеек, например, не все предложили решение доступа к ядру: eval('alert(Core.sName);',arguments.callee.caller); В принципе это сделал только один человек, а чем меньше лазеек тем лучше.
                        0
                        Здесь рассматривается именно вариант(хотя полностью не решен, будем ждать следующей статьи ;)), когда сервис предоставляет возможность сторонним разработчикам загружать их скрипты(апплеты) под управлением единого "ядра". Цель защиты, чтобы именно такой скрипт не оказался закладкой, способной перехватить вызовы "ядра". И именно такой скрипт не должен получить доступ к базовым элементам ДОМ браузера.
                          0
                          Ну вызовы ядра мы ограничим, но как вы планируете ограничивать возможности браузера? Лично я не представляю...
                            0
                            Конечно. Но смотрите в чем особенность. Это функционал по своей сути ядра(модуль загрузки апплетов). В функции безопасности ядра входит защита своей целостности! Это очень важно, так как доступ будет не только к машине пользователя, если произойдет подмена и нарушения целостности ядра, но и к центральному хранилищу.

                            Ну а безопасность на уровне пользователя, я считаю, надо организовывать по принципу сертификации и доверия к "издателю"(тому, кто подписал данный апплет). Просто мы не хотим ограничивать репозиторий апплетов только теми, что прошли нашу проверку(сертификат). Там будут все, но пользователь может выбрать по степени доверия.
                              0
                              Ну фишка ещё какбе в том, что злоумышленник может спокойно посмотреть код вашего ядра и делать потом с центральным хранилищем что угодно. Вы не думали об этом? И да, обсфукаторы довольно легко дешифруются.

                              К тому же не забывайте такую приятную мелочь как тот факт, что все JS скрипты выполняются в одном контексте в браузере и могут прототипировать любые обьекты. Умелому программеру хватит ума расширить функционал прототипов Function и Object чтобы получить полный доступ к вашему Core. Так что я не совсем понимаю смысл заниматься танцами с бубном.
                                0
                                Смысл в том что бы в какой то степени обезопасить. Меньше лазеек лучше, чем много лазеек. Темболее что инфраструктура для разработки и развертывания веб-приложений не просто в веб переносится, а в платформы обеспечивающие их функционирование и взаимодействие. И вопросы безопасноти становятся актуальными.
                                  +2
                                  Это я понимаю, но JS слишком похож на глину - что хошь, то и делай и ничто этому не преграда...

                                  Вообще я бы посоветовал почитать доки на тему того как узнавать откуда пришёл вызов функции и использовать контрольные суммы для создаваемых обьектов чтобы обезопасить свой код от модификаций. Какой-то смысл в этом есть, но стоит ли овчинка выделки так сходу не скажу, сам пока такое не делал.

                                  И ещё один совет. Вы же даёте (или будете давать) программерам какой-то API. А также возможность добавлять их код в ваш проект через некую централизованную систему. Думаю на этом этапе можно было бы добавить что-то вроде компилятора, который будет проверять какие обьекты и методы использует код. Взять тот же движок мозиллы, поковырять его, запретить вызовы некоторые и на по результату тестового выполнения сообщать программеру будете ли вы принимать его скрипт или нет.

                                  Наверное можно ещё что-то интересное придумать, но я недавно проснулся, пока туго соображаю...
                                    0
                                    Вот это офигенная идея. Единственно конечно нужно просчитать хеши для критичных к безопасности функций заранее и важно придумать где хранить хеши чтобы не было подмены. Самый простой вариант хранить хеши в базе и периодически обновлять на клиенте в фоновом режиме.
                        0
                        Извините, даже прочитать статью полностью не успел, но не могу терпеть.
                        В начале написано: "Все что описано выше..."
                        Если такое и писать, то в конце топика, в начале пишется "Все что описано ниже..."
                          0
                          Исправим.
                          0
                          Неплохо бы ещё "Applet = {};" заменить на "var Applet = {};", а то вы сами себе создаёте лазейку.
                            0
                            Ага иначе она будет глобальной. Опечатка.
                            0
                            Что-то я запутался при прочтении статьи:
                            "sCode - исполняемый код апплета, oContext - важный параметр.", а код внизу, через 2 абзаца. Мне кажется, лучше сначала код, а потом пояснение, а не наоборот.
                              0
                              Вот блин. Случайно затер этот кусок кода перед sCode - исполняемый код апплета, oContext - важный параметр
                              0
                              Простите, но это - ерунда. Можно сколько угодно изощряться в создании контекстов и т.п., расписывать красивые классы вроде Applet или Thread, но коду нельзя запретить обращаться к глобальным объектам браузера. И вся защита моментально теряет смысл. В общем, "незачет" - это не защита, а всего лишь размышления на тему того, как было бы хорошо, если бы...
                                0
                                Ну во-первых класс Applet не занимается защитой. Это класс описывающий структуру приложения созданного сторонними разработчиками. Используется для присоединения к среде в которой будет работать. Класс Thread также не занимается защитой. Его цель создавать одно или несколько пространств для выполнения кода. Пример использования - есть апплет карты и вам нужно в информаторе на карте вывести миникарту, делается второй поток и юзаются теже, загруженные скрипты.
                                Во-вторых коду можно запретить обращаться к глобальным объектам браузера ;)
                                  0
                                  > Во-вторых коду можно запретить обращаться к глобальным объектам браузера ;)
                                  как? чегото в голову не приходит как ...
                                    0
                                    Во-первых, я говорил про "классы вроде", имея в виду весь спектр различных ухищрений для изоляции отдельных участков JS-кода.
                                    Во-вторых, поделитесь пожалуйста РАБОЧИМ способом запретить ПРОИЗВОЛЬНОМУ коду обращаться к глобальным объектам браузера (модифицировать DOM, использовать innerHTML) ;)
                                      0
                                      РАБОЧИЙ пример не будет пока не будет готово то что необхдимо защищать. А если таке интересно возможноли это или нет посмотрите как сделан iGoogle (с его сотнями гаджетов от стронних разработчиков там наверное вообще одни дыры получается?), или пример Facebook platform, когда на страницу можно ставить приложения разработанные 3-ми лицами, которые также могут innerHTML заюзать и таскать конфиденцильную инфу. Но нет эти вопросы решены, скачайте Facebook platform и посмотрите как: http://developers.facebook.com/fbopen/
                                        0
                                        "РАБОЧИЙ пример не будет пока не будет готово то что необхдимо защищать."
                                        Еще раз - речь идет о запрете доступа к глобальным объектам браузера из произвольного скрипта, подключаемого на странице.

                                        "Посмотрите как сделан iGoogle..."
                                        А при чем тут iGoogle? Там разве декларируется некая "защита от модификации платформы произвольным скриптом"? Нет такого

                                        Прочитайте еще раз название статьи, плиз - и приведите работающий пример защиты. Хорошо?
                                          0
                                          > Еще раз - речь идет о запрете доступа к глобальным объектам браузера из произвольного скрипта, подключаемого на странице.

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

                                          > приведите работающий пример защиты. Хорошо?
                                          На создание полноценного secure модуля уйдет не один месяц. Для этого на стороне сервера делается ФИЛЬТР который прогоняет JS код и делает замены (было так: document.getElementById("id"), стало так: HDocument.getElementById("id"), где HDocument отностится к пространству ядра и позволяет брать только те элементы которые были созданы в КОНТЕКСТЕ АППЛЕТА, не разрешает брать чужие элементы. И т.д. так по всем объектам. Сложность состоит в том чтобы предусмотреть множество вариантов входных данных которые могут получить доступ как к глобальным объектам браузера, так и к объектам платформы.
                                            0
                                            "В моей статье речь о запрете доступа к глобальным объектам браузера не идет!"
                                            Именно поэтому статья вызывает недоумение - без контроля доступа к глобальным объектам защита платформы невозможна. Получается сферический конь - "если злоумышленник не хочет нас ломать, то для защиты мы можем..."

                                            "Для этого на стороне сервера делается ФИЛЬТР который прогоняет JS код и делает замены (было так: document.getElementById("id"), стало так: HDocument.getElementById("id")"
                                            То есть вы предлагаете преобразовать JS в некий Secure Script (это я только что придумал название), и исполнять его в рамках виртуальной машины, написанной на JS? )

                                            "Сложность состоит в том чтобы предусмотреть множество вариантов входных данных"
                                            Сложность состоит в том, что бы написать полноценную вирутальную машину, плюс своего рода "DOM в DOM'е". [b]Но эта тема к JS не имеет никакого отношения[/b], ибо исполняется код на Secure Script. Понимаете?
                                              0
                                              Какая виртуальная машина? Вы о чем? Платформа предоставляет эти объекты HDocument, HWindow и т.д. Никакой виртуальной машины совершенно. И скрипт работает с этими объектами вот и все. А так как преобразование идет на стадии когда Script еще является текстом и только после преобразование уходит на конструктор Function, то вероятность взлома >>сильно уменьшается.
                                                0
                                                "Платформа предоставляет эти объекты HDocument, HWindow и т.д"
                                                А что вы будете заменять в этом коде:

                                                (function() {
                                                var x = function(){};
                                                var y = x.constructor;
                                                var a = "win";
                                                var b = "dow.a";
                                                var c = "lert('h";
                                                var d = "ack');";
                                                (new y(a + b + c + d))();
                                                })();

                                                Очевидно, что ссылок на глобальные объекты код в явном виде не содержит - они формируются во время выполнения. И не раньше.
                                                  0
                                                  Очень просто. Было сказано что переопределаются объекты и функции, а темболее такие как eval и оператор new. В нашем случае после прохода фильтром мы имеем:

                                                  (New(y(a + b + c + d)))(), где New занимается созданием любых новых объектов в платформе, так эта часть y(a + b + c + d) у нас сформириуется во время выполнения, платформа перехватит аргумент поступивший в функцию New, далее этот же аргумент в виде текста проходит фильтр и создается уже объект, в нашем же случае можно сразу пометить такой код как вредоносный и не продолжать работу, так как для разработчиков будут предусмотрены объекты платформы (HWindow, HDocument и т.д.) имеющие тотже интерфейс что и нативные объекты, то если кто то пишет window.alert('Hack') игнорируя предусмотренные объекты, то у него явно нехорошие намерения.
                                                    0
                                                    Такой подход в теории может решить проблему но
                                                    а) Если предполагается разрешать работу с DOM-ом потребуется создать гигансткое количество врапперов.
                                                    б) достаточно добавить к вышенаписанному коду
                                                    var New = function(a){return new a;};
                                                    и фильтрация идет боком, таких варинатов много - есть масса точек для атаки.
                                                      0
                                                      Нужно перехватывать вызовы функций, чтений/установку свойств, и много чего еще ;) Кстати, что делать с зарезервированными словами, которых достаточно много - тупо запрещать их использование? Т.е. alert('check document') автоматически становится вредоносным кодом )
                                                      0
                                                      Ну так даже не интересно. Вуаля (слова new нет):
                                                      (function() {
                                                      var x = function(){};
                                                      var y = x.constructor;
                                                      var a = "win";
                                                      var b = "dow.a";
                                                      var c = "lert('h";
                                                      var d = "ack');";
                                                      (y(a + b + c + d))();
                                                      })();

                                                      Теперь вам в обязательном порядке прийдется контролировать каждый вызов функции, проверяя в рантайме типы аргументов и их значения.

                                                      Именно такой подход называется виртуальной машиной.
                                                        0
                                                        В теории это все решается (в вашем случае фильтруется function и нативный параметр construct).

                                                        Но вот это все равно не виртуальная машина. Где в этом подходе вы видите семантический анализ и последующее исполнение P-кода на виртуальной машине (допустим стековой архитектуры) это ничего не было заявлено предлагались только фильтры.
                                                          0
                                                          "В теории это все решается (в вашем случае фильтруется function и нативный параметр construct)."
                                                          Каким образом фильтруется function? Вы не можете запретить мне создавать функции, равно как и использовать constructor - ибо в этом случае мы говорим уже не о JS, а о чем-то кастрированном.

                                                          "Но вот это все равно не виртуальная машина."
                                                          Это виртуальная машина - т.к. вам потребуется САМОСТОЯТЕЛЬНО реализовать все стандартные элементы языка, такие как (), new, [] и все-все-все. После чего в рантайме вы производите анализ аргументов, при необходимости преобразуете их в вид понятный браузерному интерпритатору, и отдаете на исполнение.

                                                          Или можно сказать по-другому - вы преобразуете JS в некий Safe Script, который потом и исполняется написанным вами интерпретатором.
                                                            0
                                                            Да нет никакого итерпретатора. Вы хоть их писали? Я писал с нуля на С++ для С++ like script. А здесь предлагается решение из врапперов оберточных функций), где нативные методы оборачиваются функциями в которых фильтруется входная информация).
                                                              0
                                                              Расскажите пожалуйста, как "только врапперами, которые фильтруют" реализовать обработку исключений?
                                                                0
                                                                Мне кажется вы не поняли на каком этапе происходит подмена. Это не в риалтайме. А когда загружается код внешнего апплета, именно в этот момент все конструкции, которые могут вызвать выполнение или загрузку сотороннего кода заменяются в коде на наши функции(которые уже будут фильтровать входящие параметры, которые могут генерироваться динамически).
                                                                  0
                                                                  Речь идет о том, что в риалтайме потребуется анализировать каждый чих. Та самая фильтрация - что разрешать, а что запрещать.

                                                                  Отсюда огромный объем работы - а не "прстая" фильтрация слов window и document.

                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                  Самое читаемое