Открыть список
Как стать автором
Обновить

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

Мне кажется в последнее время через чур много внимания уделяют «псевдо» плохости глобальных переменных и в «псевдо» удобности отсутствия оных в JS.
Если понимать глобальную переменную как ссылку на модуль — это не плохо, так проще организовывать структуру проекта. Если глобальная переменная как локальная переменная (отсутствие var) — казнить, нельзя помиловать!
Отсутствие глобальных переменных это хорошо из-за соображений безопасности: злоумышленнику гораздо сложнее анализировать код, невозможно вызвать внутренний метод напрямую.
Злоумышленнику сложнее анализировать JavaScript-код, поставляемый в открытом виде?
Про открытый вид я ничего не говорил. Значительно проще анализировать код если ты видишь глобальные переменные/объекты (есть с чего начать). Разве не так?
А ничего что некоторые браузеры не позволяют сделать delete для свойств объекта window?
Досадное упущение, добавил хак для IE.
надеюсь только для IE ниже 9 версии?
И можно узнать какой?
Я бы сказал, что отсутствие глобальных переменных осложняет злоумышленнику модификацию вашего кода на машине конечно пользователя.
Т.е. даже если у него есть возможность на странице с вашим кодом (хорошим) выполнить свой код (плохой), то, при отсутствии глобальных переменных в хорошем коде, ему будет сложнее как-то вмешаться в его работу программно из плохого. Если вы будете использовать глобальную jQuery, к примеру, то, модифицировав ее методы, он сможет менять логику работы вашего кода и т.п.
Если вы боитесь за сохранность jQuery, то лучше включить её в сборку(будет 1 монолитный js файл).
Я это понимаю, просто своими словами объяснил в чем выгода безопасности от отсутствия глобалов =)
Не поверите, сейчас в _любом_ браузере есть дебаггер, злоумышленник может анализировать код даже без глобальных переменных.
Дело не в анализе кода, а в затруднении «инъекций» в этот код со стороны чужого кода на стороне конечного пользователя.
Интересная штука, хотя и очень специализированная.
Ну и 3й метод для предотвращения перехватов, ни разу не unobtrusive, к чему стремится остальное, может очистить какие-то нужные таймеры/интервалы.
Писал нечто подобное свое, но там в замыкание собирались все необходимые файлы и все (ну плюс всякие преобразования кода необходимые).
Согласен, правилом должно быть «не трогать глобальные переменные». Если кто-то юзает до тебя таймеры значит это ему нужно и таким действием можно только всё сломать.

Тоже самое и удалять за собой глобальные объекты. Если они какое-то время видны, а потом их нестало это совсем не тоже самое что «их небыло». Даже на времени жизни этих переменных от объявления до удаления может произойти всё что угодно. И это не считая того, что уже при объявлении имена могли совпасть с чем-то существующим.

В целом я за использование своего собственного неймспейса при необходимости работать с глобальными переменными. Это наиболее безопасный и правильный способ.
Насчет убийства таймеров я с вами могу не согласиться. Если вы применяете эту технику, значит вам есть от чего защищаться (юзерскрипты и расширения которые работают с вашей страницей), поэтому убив чужие таймеры вы только улучшите вашу защиту.
Если они какое-то время видны
Они видны только скрипту во время первичного его скрипта. Они не видны до и не видны после отработки скрипта. Зачем их оставлять, если вы знаете, что их ничего и никто(разработчик) не будет испльзовать?
Учитывайте, что в статье описана сборка для Production.
Что делать в случае, если методы таймеров были переопределены каким-то скриптом?
Или здесь обязательно оговорка, что этот скрипт отрабатывает 100% всегда первым?
Или здесь обязательно оговорка, что этот скрипт отрабатывает 100% всегда первым?
Нет.
Что делать в случае, если методы таймеров были переопределены каким-то скриптом?
Вот простейший способ определить это:
/\s*\[native code]/.test(window.setTimeout);

Однако злоумышленник может сделать вот так и испортить всю малину:
RegExp.prototype.test = function () {return 4};

Дальше это уже другая история. У меня есть другой проект, в котором есть функции isNativeFunction, которые не используют RegExp#test.

Я специально не стал включать функции защиты от перехвата переменных в текущую версию — они требуют дополнительной работы.
window.clearInterval= frame.contentWindow.clearInterval
Предположим, что мы получили потенциально чистый setInterval via:
var fr = document.createElement('iframe');
fr.src='about:blank';
document.body.appendChild(fr);
fr.contentWindow.setInterval

1. setInterval текущего окна и фрэйма имеют разные счетчики таймеров. Это не такая уж и беда.
2. Мы не защищены от перехвата фрэйма во время вставки в DOM (при вставке в DocumentFragment frame не создает окно).
3. Думаю, что так или иначе можно отравить и frame.contentWindow.clearInterval/setInterval в about:blank

Единственное, что не может переопределить пользователь это поведение операторов, на них и стоит в первую очередь опираться.
Ерундой занимаетесь по моему. Безопасность в JS отсутствует по причине наличия прототипов. Можно в легкую переопределить прототипы всех базовых классов (типа String, Array и остальных), а в методах реализовать все что угодно, и даже (о чудо) такое:

(function(method) {
  for (var method in document) {
    (function(method) {
      var toString = document[method] + "";

      document[method] = function() {
        alert("Ня");
      };

      document[method].toString = function() { return toString; };
      document[method].toSource = function() { return toString; };
    })(method);
  }
})();



Попробуйте создать фрейм через document.createElement.

Правильно применив такой «взлом», внести вредоносный код в ваше приложение будет несложной задачей.
Запустил, то что написал. Поправка:

<code>
(function(method) {
  for (var method in document) {

    if (typeof document[method] != "function") { continue; }

    (function(method) {
      var toString = document[method] + "";

      document[method] = function() {
        alert("Ня");
      };

      document[method].toString = function() { return toString; };
      document[method].toSource = function() { return toString; };
    })(method);
  }
})();
</code>
Я вкурсе, что можно сломать все. Единственное, что не может сломать пользователь напрямую из скрипта — UserAgent (не опера) и примитивные операторы.
Задача же isNativeFunction состоит в том, чтобы выяснить были ли переопределены методы и оповестить об этом наблюдателя (дальше основная логика скрипта не будет выполнена). Про toString и toSource и прочие я, конечно, знаю и с этим можно бороться.

Прототип isNativeFunction точно работает в FF. Взломом будет считаться переопределение toString, hasOwnProperty, присутствие toString у метода и прочие подозрительные операции, которые обычный пользователь не будет писать в любом случае.
function isNativeFunction(functionSource, functionName) {
    var nativeCodeToString = (function(){
            if (window.opera || typeof /./ === 'function') { // Op Ch fix
                return 'function toString() { [native code] }';
            }
            return 'function toString() {\n    [native code]\n}';
        }()),
        nativeCodeFunction = (function(){
            if (window.opera || typeof /./ === 'function') { // Op Ch fix
                return 'function ' + functionName + '() { [native code] }';
            }
            return 'function ' + functionName + '() {\n    [native code]\n}';
        }()),
        nativeCodeHasOwnProperty = (function(){
            if (window.opera || typeof /./ === 'function') { // Op Ch fix
                return 'function ' + functionName + '() { [native code] }';
            }
            return 'function hasOwnProperty() {\n    [native code]\n}';
        }());

    return (functionSource.toString + '' === nativeCodeToString) &&
    (functionSource.hasOwnProperty + '' === nativeCodeHasOwnProperty) &&
    (functionSource + '' === nativeCodeFunction) &&
    (!functionSource.hasOwnProperty('toString'))  &&
    (Function.prototype.toString + '' === nativeCodeToString)  &&
    (Function.prototype.toString === Function.toString)  &&
    (!Function.prototype.toString.hasOwnProperty('toString'))  &&
    (Object.prototype.toString + '' === nativeCodeToString)  &&
    (!Object.prototype.toString.hasOwnProperty('toString'))  &&
    (typeof Function === 'function')  &&
    (typeof Object === 'function');
};
Вы не только защититесь, но и сломаете юзерскрипты/расширения. Это конечно метод защиты, но крутоват.

> Они не видны до и не видны после отработки скрипта

Но они могут перетереть глобальные других скриптов с такими же названиями.
Лучше все же сразу при сборке собирать все в одно замыкание и все.
Пожалуйста, не называйте всё и всех «ниндзя»-производными. Это «звание» нужно заслужить, не присвоить.
Ни в коему случае не хочу посягать на этот титул.
Ninjs — назван так по способу работы, я это объяснил в посте. Я с удовольствием рассмотрю ваши варианты.
А техника «Сокрытие глобальных переменных», я считаю, названа по достоинству техникой Ninja.
Верно, нужно хорошо маскироваться, а то если кто-то случайно постигнет твои секретные/запретные техники, то потом следую кодексу нинзя придется его убить, а нам не нужны такие хлопоты :)
ага,
(function(){
  // ниндзя убил жертву
})();
// никто не видел ниндзю
Обычно вполне устраивает использование нейспейсов, например в YUI библиотеки YAHOO.util.Dom, YAHOO.lang и тд. Иногда проще сделать проще, чем потом производить в ранг нинзя другого человека.
Но в любом случае JS это занимательно, всегда есть куда применить здоровые и не очень идеи :)
Я ни в коем случае не пропагандирую затруднение разработки во благо скрытия глобалов just for fun.
В статье была показана сборка проекта для среды Production — там потенциальные злоумышленники от которых мы хотим защититься, затрудняя reverse engineering и скрывая глобалы. Для среды Deveopment вы можете создать свою сборку с открытыми глобалами.
Оффтопик:
Ниндзя — это шпионы. Их задачи, в основном — шпионаж и убийства. Ваша библиотека — скорее контрразведка, борьба со шпионами врага. Думаю, что круче было бы назвать тулзу KGBjs или NKVDjs (FSBjs, как вариант)
Да, как вариант! Или FBIjs для западных пользователей.
KGB — одинаково узнаваемый бренд и для нас, и для запада
var $my= { _my: $my }

with( $my ){
$my.$= function( id ){
return document.getElementsById( id )
}
}

with( $my ){
$my.hide= function( id ){
$( id ).style.visibility= 'hidden'
}
}

$my= $my._my

объявлять модули можно в любом порядке, сторонний код не замечает нашего присутствия. а от «ух, злобных хакеров» всё-равно не защититься, так что не будем страдать маразмом и дропать все таймеры и прочая
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.