Safe
СОДЕРЖАНИЕ
ИМЯ
Safe — Компиляция и выполнение кода в ограниченных отсеках
СИНТАКСИС
use Safe;
$compartment = new Safe;
$compartment->permit(qw(time sort :browse));
$result = $compartment->reval($unsafe_code); ОПИСАНИЕ
Модуль расширения Safe позволяет создавать отсеки, в которых можно оценивать код Perl. Каждый отсек имеет
- новый пространство имён
-
«Корень» пространства имён (т. е. «main::») меняется на другой пакет, и код, оцениваемый в отсеке, не может ссылаться на переменные за пределами этого пространства имён, даже с помощью глобальных поисков во время выполнения и других хитростей.
Код, скомпилированный за пределами отсека, может поместить переменные в пространство имён отсека (или поделиться переменными) и только эти данные будут видны коду, оцениваемому в отсеке.
По умолчанию, единственными переменными, которые разделяются с отсеками, являются переменные «нижнего подчеркивания» $_ и @_. (и, технически, реже используемые %_, файловый дескриптор _ и т. д.). Это связано с тем, что в противном случае операторы Perl, по умолчанию использующие $_, не будут работать, как и присвоение аргументов @_ при входе в подпрограмму.
- маску операторов
-
Каждый отсек имеет связанную «маску операторов». Вспомните, что код Perl компилируется во внутренний формат перед выполнением. Оценка кода Perl (например, с помощью «eval» или «do 'file'») приводит к компиляции кода во внутренний формат, а затем, при отсутствии ошибок в компиляции, к выполнению. Код, оцениваемый в отсеке, компилируется с учётом маски операторов отсека. Попытка оценить код в отсеке, содержащем замаскированный оператор, приведёт к сбою компиляции с ошибкой. Код не будет выполнен.
По умолчанию маска операторов для нового отсека — это опкод ':default'.
Важно ознакомиться с документацией модуля Opcode для получения дополнительной информации, особенно для подробных определений имен опкодов, меток и наборов опкодов.
Поскольку маска операторов применяется только на этапе компиляции, контролируемый доступ к потенциально небезопасным операциям может быть реализован путём размещения обработчика подпрограммы (написанного за пределами отсека) в отсеке. Например,
$cpt = new Safe; sub wrapper { # vet arguments and perform potentially unsafe operations } $cpt->share('&wrapper');
ПРЕДУПРЕЖДЕНИЕ
Модуль Safe не реализует эффективную песочницу для оценки ненадежного кода с интерпретатором Perl.
Ошибки в интерпретаторе Perl, которые можно использовать для обхода ограничений Safe, не рассматриваются как уязвимости. См. perlsecpolicy для получения дополнительной информации.
Авторы не дают никаких гарантий, явных или подразумеваемых, относительно пригодности данного программного обеспечения для целей безопасности или безопасности.
Авторы в любом случае не несут ответственности за особые, случайные, косвенные или другие аналогичные убытки, возникающие в результате использования данного программного обеспечения.
Ваши результаты могут отличаться. В случае сомнений не используйте его.
МЕТОДЫ
Для создания нового отсека используйте
$cpt = new Safe; Необязательный аргумент (NAMESPACE), где NAMESPACE — корневое пространство имён для использования в отсеке (по умолчанию «Safe::Root0», увеличивается для каждого нового отсека).
Обратите внимание, что версия 1.00 модуля Safe поддерживала второй необязательный параметр MASK. Эта функциональность была удалена до более глубокого рассмотрения. Используйте методы permit и deny, описанные ниже.
Следующие методы могут затем использоваться с объектом отсека, возвращаемым вышеупомянутым конструктором. Объект аргумент подразумевается в каждом случае.
permit (OP, ...)
Разрешить указанные операторы для использования при компиляции кода в отсеке (в дополнение к уже разрешённым операторам).
Вы можете перечислять опкоды по именам или использовать имя тега; см. "Предопределённые метки опкодов" в Opcode.
permit_only (OP, ...)
Разрешить только указанные операторы для использования при компиляции кода в отсеке (никакие другие операторы не разрешены).
deny (OP, ...)
Запретить указанные операторы для использования при компиляции кода в отсеке (другие операторы могут всё ещё быть разрешены).
deny_only (OP, ...)
Запретить только указанные операторы для использования при компиляции кода в отсеке (все остальные операторы будут разрешены, поэтому, скорее всего, вам не следует использовать этот метод).
trap (OP, ...), untrap (OP, ...)
Методы trap и untrap являются синонимами для deny и permit соответственно.
share (NAME, ...)
Это разделяет переменные в списке аргументов с отсеком. Это почти идентично экспорту переменных с использованием модуля Exporter.
Каждый NAME должен быть именем нелексической переменной, обычно с включенным ведущим идентификатором типа. Голое слово обрабатывается как имя функции.
Примеры допустимых имен: '$foo' для скаляра, '@foo' для массива, '%foo' для хеша, '&foo' или 'foo' для подпрограммы и '*foo' для глобуля (т. е. все записи таблицы символов, связанные с "foo", включая скаляры, массивы, хеши, подпрограммы и файловые дескрипторы).
Каждый NAME предполагается в вызывающем пакете. См. share_from для альтернативного метода (который share использует).
share_from (PACKAGE, ARRAYREF)
Этот метод похож на share(), но позволяет явно указать пакет, из которого следует разделять символы. Имена символов (включая символы типа) передаются в виде массива ссылок.
$safe->share_from('main', [ '$foo', '%bar', 'func' ]); Имена могут содержать имена пакетов, которые являются относительными по отношению к указанному PACKAGE. Таким образом, эти два вызова имеют тот же эффект:
$safe->share_from('Scalar::Util', [ 'reftype' ]);
$safe->share_from('main', [ 'Scalar::Util::reftype' ]); varglob (VARNAME)
Это возвращает ссылку на глобуля для записи в таблице символов VARNAME в пакете отсека. VARNAME должен быть именем переменной без ведущего маркера типа. Например:
${$cpt->varglob('foo')} = "Hello world"; имеет тот же эффект, что и:
$cpt = new Safe 'Root';
$Root::foo = "Hello world"; но избавляет от необходимости знать имя пакета $cpt.
reval (STRING, STRICT)
Это оценивает STRING как код Perl внутри отсека.
Код может видеть только пространство имён отсека (как возвращает метод root). Корневой пакет отсека кажется коду внутри отсека как main:: пакет.
Любая попытка кода в STRING использовать оператор, не разрешённый отсеком, приведёт к ошибке (во время выполнения основной программы, но во время компиляции для кода в STRING). Ошибка имеет вид "'%s' trapped by operation mask...".
Если такой заблокированный оператор встречается или любая другая ошибка во время компиляции или возврата, то $@ устанавливается на сообщение об ошибке, как и в случае с eval().
Если ошибок нет, то метод возвращает значение последнего вычисленного выражения или может быть использован оператор возврата, как и в подпрограммах и eval(). Контекст (список или скаляр) определяется вызывающим объектом как обычно.
Если значение возврата reval() является (или содержит) ссылкой на код, эти ссылки на код оборачиваются так, чтобы их выполнение всегда происходило в отсеке. См. "wrap_code_refs_within".
Прежнее необъявленное аргумент STRICT устанавливает строгость: если истинно, используется 'use strict;', в противном случае используется 'no strict;'. Примечание: если STRICT опущен, по умолчанию используется 'no strict;'.
Некоторые моменты, которые следует учесть:
Если оператор entereval разрешён, то код может использовать eval "..." для «скрытия» кода, который может использовать запрещённые операторы. Это не является большой проблемой, так как когда код пытается выполнить eval, он потерпит неудачу, потому что маска операторов по-прежнему активна. Однако с помощью этой техники хитрый и, возможно, вредный код может «зондировать» пределы возможного.
Любой строковый eval, который выполняется кодом, выполняющимся в отсеке, или кодом, вызываемым кодом, выполняющимся в отсеке, будет eval'ed в пространстве имён отсека. Это потенциально серьёзная проблема.
Представьте себе функцию foo() в пакете pkg, скомпилированную за пределами отсека, но общую с ним. Предположим, что у отсека есть корневой пакет под названием 'Root'. Если foo() содержит оператор eval, такой как eval '$foo = 1', то обычно $pkg::foo будет установлен в 1. Если foo() вызывается из отсека (любым способом), то вместо установки $pkg::foo, eval фактически установит $Root::pkg::foo.
Это легко продемонстрировать, используя модуль, например модуль Socket, который использует eval "..." в качестве части функции AUTOLOAD. Вы можете «использовать» модуль за пределами отсека и поделиться функцией (AUTOLOAD) с отсеком. Если AUTOLOAD запускается кодом в отсеке или любым кодом в любом месте, который вызывается любым способом из отсека, то eval в функции AUTOLOAD модуля Socket происходит в пространстве имён отсека. Любые переменные, созданные или используемые eval'ed кодом, теперь находятся под контролем кода в отсеке.
Аналогичный эффект относится к всем операциям поиска символов во время выполнения в коде, вызываемом из отсека, но не скомпилированном в нём.
rdo (FILENAME)
Это оценивает содержимое файла FILENAME внутри отсека. Он использует те же правила, что и встроенный perl's do для поиска файла, возможно, используя @INC.
См. документацию метода reval для получения дополнительной информации.
root (NAMESPACE)
Этот метод возвращает имя пакета, являющегося корнем пространства имён отсека.
Обратите внимание, что это поведение отличается от версии 1.00 модуля Safe, где корневой модуль мог использоваться для изменения пространства имён. Эта функциональность была изъята до дальнейшего рассмотрения.
mask (MASK)
Это метод get-or-set для маски оператора отсека.
При отсутствии аргумента MASK он возвращает текущую маску оператора отсека.
При наличии аргумента MASK он устанавливает маску оператора для отсека (эквивалентно вызову метода deny_only).
wrap_code_ref (CODEREF)
Возвращает ссылку на анонимную подпрограмму, которая, при выполнении, вызовет CODEREF с отсеком Safe «в действии». Другими словами, с настроенным пространством имён пакета и включённой маской оператора.
Обратите внимание, что маска оператора не влияет на уже скомпилированный код, она влияет только на последующую компиляцию, которую уже скомпилированный код может попытаться выполнить.
Это особенно полезно, когда применяется к ссылкам на код, возвращённым из reval().
(Это также предоставляет своего рода обходной путь для RT#60374: «Ошибка сортировки Safe.pm {} с -Dusethreads». См. https://rt.perl.org/rt3//Public/Bug/Display.html?id=60374 для более подробной информации.)
wrap_code_refs_within (...)
Оборачивает все ссылки CODE, найденные в аргументах, заменяя каждую из них результатом вызова wrap_code_ref на ссылке CODE. Любые ссылки ARRAY или HASH в аргументах проверяются рекурсивно.
Не возвращает ничего.
РИСКИ
Этот раздел представляет собой лишь общую схему некоторых действий, которые код в отсеке может выполнить (намеренно или непреднамеренно), что может повлиять на область за пределами отсека.
- Память
-
Потребление всей (или почти всей) доступной памяти.
- Процессор
-
Вызов бесконечных циклов и т.п.
- Шпионаж
-
Копирование конфиденциальной информации из вашей системы. Даже что-то столь простое, как ваше имя пользователя, имеет ценность для других. Например, из ваших переменных среды можно извлечь много полезной информации.
- Сигналы
-
Вызов сигналов (особенно SIGFPE и SIGALARM) для воздействия на ваш процесс.
Настройка обработчика сигналов требует тщательного рассмотрения и контроля. Какая маска активна, когда вызывается обработчик сигнала? Если пользователь может заставить импортированную функцию получить исключение и вызвать обработчик сигнала пользователя, восстанавливается ли ограниченная маска пользователя до вызова обработчика? Вызывается ли импортированный обработчик со своей исходной маской или с маской пользователя?
- Изменение состояния
-
Операции, такие как chdir, очевидно, влияют на весь процесс, а не только на код в отсеке. Операции, такие как rand и srand, имеют аналогичное, но более тонкое воздействие.
АВТОР
Изначально разработан и реализован Malcolm Beattie.
Переработан с использованием модуля Opcode и другими изменениями, добавленными Tim Bunce.
В настоящее время поддерживается Perl 5 Porters, <perl5-porters@perl.org>.
© 1993–2021 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.36.0/Safe