Wiki-учебник по веб-технологиям: JavaScript/НаписаниеСкрипта ...

Главная | Каталог |

Написание скрипта


В этом разделе специально рассматривается с написание маркированных скриптов.

Захват событий из других серверов


Если окно с фрэймами должно захватывать события на страницах с других серверов, используйте метод enableExternalCapture в маркированном скрипте, запрашивающем привилегии UniversalBrowserWrite.

Используйте этот метод перед вызовом метода captureEvents. Например, с помощью следующего кода окно может захватывать все события Click во всех своих фрэймах.

<SCRIPT ARCHIVE="myArchive.jar" ID="archive">
...
function captureClicks() {
   netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserWrite"); 
   enableExternalCapture();
   captureEvents(Event.CLICK);
   ...
}
...
</SCRIPT>


Изолирование немаркированного слоя внутри маркированного контейнера


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


Интернациональные символы в маркированных скриптах


При использовании в скриптах интернациональные символы могут появляться в строковых константах и в комментариях. Ключевые слова и переменные JavaScript не могут содержать интернациональных специальных символов.

Скрипты, содержащие символы других языков (интернациональные), не могут быть промаркированы, потому что процесс трансформации символов в локальный набор символов дезавуирует подпись. Чтобы обойти это ограничение:


Для HTML, окружающего маркированные скрипты, ограничений на символы других языков нет.

Функции импорта и экспорта


Вам могут понадобиться интерфейсы для вызова в засекреченных контейнерах (окнах и слоях). Для этого используйте операторы import и export. Экспортирование имени функции делает её доступной для импорта скриптами, находящимися за пределами контейнера, без проверки контейнера.

Вы можете импортировать и экспортировать только функции – как функции верхнего уровня (ассоциированные с объектом window), так и методы некоторых других объектов. Вы не можете импортировать или экспортировать объекты целиком или свойства, которые не являются функциями.

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

Для использования import и export вы обязаны явно установить атрибут LANGUAGE тэга SCRIPT в «JavaScript1.2":

<SCRIPT LANGUAGE="JavaScript1.2">


В маркированном скрипте, который определяет функцию, доступ к которой вы хотите дать другим скриптам, используйте оператор export. Синтаксис этого оператора:

exportStmt ::= export exprList
exprList ::= expr | expr, exprList


где каждое expr обязано разрешаться в имя функции. Оператор export маркирует каждую функцию как импортируемую.

В скрипте, в который вы хотите импортировать функцию, используйте оператор import. Синтаксис этого оператора:

importStmt ::= import importList
importList ::= importElem | importElem, importList
importElem ::= expr.funName | expr.*


Выполнение import expr.funName вычисляет expr и импортирует функцию funName объекта в пределах текущей области видимости. Будет ошибкой, если expr не вычисляется в объект, если нет функции по имени funName или если функция существует, но не маркирована как импортируемая. Выполнение import expr импортирует все импортируемые функции из expr.

Пример

В это примере имеются три страницы в наборе фрэймов. Файл containerAccess.html определяет этот набор фрэймов и вызывает пользовательскую функцию, когда набор фрэймов загружен. Одна страница, secureContainer.html, содержит маркированный скрипт и экспортирует функцию. Другая страница, access.html, импортирует экспортируемую функцию и вызывает её.

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

Файл containerAccess.html содержит следующий код:

<HTML>
<FRAMESET NAME=myframes ROWS="50%,*" onLoad="inner.myOnLoad()">
<FRAME NAME=inner SRC="access.html">
<FRAME NAME=secureContainer SRC="secureContainer.html">
</FRAMESET>
</HTML>


Файл secureContainer.html содержит следующий код:

<HTML>
This page defines a variable and two functions. 
Only one function, publicFunction, is exported.
<BR>
<SCRIPT ARCHIVE="secureContainer.jar" LANGUAGE="JavaScript1.2" ID="a">
function privateFunction() {
   return 7;
}
var privateVariable = 23;
function publicFunction() {
   return 34;
}
export publicFunction;
netscape.security.PrivilegeManager.enablePrivilege(
   "UniversalBrowserRead");
document.write("This page is at " + history[0]);
// привилегии отменяются автоматически при окончании работы скрипта
</SCRIPT>
</HTML>


Файл access.html содержит следующий код:

<HTML>
This page attempts to access an exported function from a signed
container. The access should succeed.
<SCRIPT LANGUAGE="JavaScript1.2">
function myOnLoad() {
   var ctnr = top.frames.secureContainer;
   import ctnr.publicFunction;
   alert("value is " + publicFunction());
}
</SCRIPT>
</HTML>


Подсказки для написания безопасного JavaScript


Проверка размещения скрипта

Если вы промаркировали скрипты на страницах, размещённых на вашем сайте, можно скопировать JAR-файл с вашего сайта и поместить его на другой сайт. Поскольку сами по себе маркированные скрипты не изменились, они будут продолжать работу под вашей подписью.

Если вы хотите предотвратить это, вы может заставить ваши скрипты работать только с вашего сайта.

<SCRIPT ARCHIVE="siteSpecific.jar" ID="a" LANGUAGE="JavaScript1.2">
if (document.URL.match(/^http:\/\/www.company.com\//)) {
   netscape.security.PrivilegeManager.enablePrivilege(...);
   // здесь начинка скрипта
}
</SCRIPT>


Тогда, если JAR-файл и скрипт скопированы на другой сайт, они не будут больше работать. Если тот, кто скопировал скрипт, изменит его, чтобы обойти проверку источника скрипта, подпись дезавуируется.

Будьте осторожны с тем, что экспортируете

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

<SCRIPT ARCHIVE="duh.jar" ID="a">
function myEval(s) {
   netscape.security.PrivilegeManager.enablePrivilege(
      "UniversalFileAccess");
   return eval(s);
}
export myEval; // НЕ ДЕЛАЙТЕ ЭТОГО!!!!
</SCRIPT>


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

Минимизация Trusted Code Base

На жаргоне системы безопасности, trusted code base (TCB) это набор кода, имеющий привилегии для выполнения ограниченных акций. Одним из путей повышения безопасности является уменьшение размера TCB, что даст меньше возможностей для атак или ошибок.

Например, следующий код, если он выполняется в маркированном скрипте с пользовательским одобрением, открывает новое окно, содержащее историю браузера:

<SCRIPT ARCHIVE="historyWin.jar" ID="a">
netscape.security.PrivilegeManager.enablePrivilege(
   "UniversalBrowserAccess");
var win = window.open();
for (var i=0; i < history.length; i++) {
   win.document.writeln(history[i] + "<BR>");
}
win.close();
</SCRIPT>


TCB в этом примере это весь скрипт, поскольку выданы в начале и нигде не отменяются. Вы можете уменьшить TCB, переписав программу так:

<SCRIPT ARCHIVE="historyWin.jar" ID="a">
var win = window.open();
netscape.security.PrivilegeManager.enablePrivilege(
   "UniversalBrowserAccess");
for (var i=0; i < history.length; i++) {
   win.document.writeln(history[i] + "<BR>");
}
netscape.security.PrivilegeManager.revertPrivilege(
   "UniversalBrowserAccess");
win.close();
</SCRIPT>


После этого TCB – это только цикл, содержащий доступ к свойству history. Вы можете исключить излишние вызовы Java, изменяя привилегию с помощью ввода функции:

<SCRIPT ARCHIVE="historyWin.jar" ID="a">
function writeArray() {
   netscape.security.PrivilegeManager.enablePrivilege(
      "UniversalBrowserAccess");
   for (var i=0; i < history.length; i++) {
      win.document.writeln(history[i] + "<BR>");
   }
}
var win = window.open();
writeArray();
win.close();
</SCRIPT>


Привилегии автоматически изменяются, когда writeArray возвращает управление, поэтому вам нет необходимости делать это явным образом.

Использование минимума, необходимого для решения задачи

Другой способ уменьшить возможность проникновения и возникновения ошибок – использовать только необходимый минимум, необходимый для выполнения данного доступа. Например, предыдущий код запрашивал UniversalBrowserAccess, которая является целевым макросом, содержащим UniversalBrowserRead и UniversalBrowserWrite. Только UniversalBrowserRead требуется для чтения элементов из массива history, поэтому вы может переписать вышеприведённый код и сделать его более безопасным:

<SCRIPT ARCHIVE="historyWin.jar" ID="a">
function writeArray() {
   netscape.security.PrivilegeManager.enablePrivilege(
      "UniversalBrowserRead");
   for (var i=0; i < history.length; i++) {
      win.document.writeln(history[i] + "<BR>");
   }
}
var win = window.open();
writeArray();
win.close();
</SCRIPT>