Safe
СОДЕРЖАНИЕ
НАЗВАНИЕ
Safe — Компиляция и выполнение кода в ограниченных отсеках
СИНОПСИС
use Safe;
$compartment = new Safe;
$compartment->permit(qw(time sort :browse));
$result = $compartment->reval($unsafe_code); ОПИСАНИЕ
Модуль расширения Safe позволяет создавать отсеки, в которых можно оценивать Perl-код. Каждый отсек имеет
- новое пространство имён
-
«Корень» пространства имён (т. е. «main::») изменяется на другой пакет, и код, выполняемый в отсеке, не может ссылаться на переменные за пределами этого пространства имён, даже с использованием глобальных поисков во время выполнения и других хитростей.
Код, скомпилированный вне отсека, может поместить переменные в (или разделить переменные с) пространством имён отсека, и только эти данные будут видны коду, выполняемому в отсеке.
По умолчанию, с отсеками разделяются только переменные «подчёркивания» $_ и @_. (и, технически, реже используемые %_, _ filehandle и т. д.). Это потому, что в противном случае операторы Perl, по умолчанию использующие $_, не будут работать, также как и присваивание аргументов @_ при входе в подпрограмму.
- маску операторов
-
Каждый отсек имеет связанную «маску операторов». Помните, что Perl-код компилируется в внутренний формат перед выполнением. Выполнение Perl-кода (например, с помощью «eval» или «do 'файл'») вызывает компиляцию кода во внутренний формат, а затем, при отсутствии ошибок компиляции, его выполнение. Код, выполняемый в отсеке, компилируется с учётом маски операторов отсека. Попытка выполнения кода в отсеке, содержащем замаскированный оператор, приведёт к ошибке компиляции. Код не будет выполнен.
По умолчанию, маска операторов для нового отсека — ':default' optag.
Важно ознакомиться с документацией модуля Opcode для получения дополнительной информации, особенно для подробных определений opname, optag и opset.
Поскольку маска операторов применяется только на стадии компиляции, контролируемый доступ к потенциально небезопасным операциям можно получить, поместив в отсек ссылку на оберточную подпрограмму (написанную вне отсека). Например,
$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, ...)
Разрешить указанные операторы для использования при компиляции кода в отсеке (в дополнение к уже разрешённым операторам).
Вы можете перечислять операторы по именам или использовать имя тега; см. "Predefined Opcode Tags" в 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…».
Если операция заблокирована таким образом, код в STRING не будет выполнен. Если такая заблокированная операция происходит или возникает любая другая ошибка во время компиляции или возврата, $@ устанавливается в сообщение об ошибке, как и в случае с eval().
Если ошибки нет, метод возвращает значение последнего вычисленного выражения или может использоваться оператор return, как и в подпрограммах и eval(). Контекст (список или скаляр) определяется вызывающим объектом, как обычно.
Если возвращаемое значение reval() является (или содержит) ссылкой на код, эти ссылки на код обертываются, чтобы их выполнение всегда происходило в отсеке. См. "wrap_code_refs_within".
Ранее недокументированный аргумент STRICT устанавливает строгость: если он имеет значение true, используется 'use strict;', в противном случае — 'no strict;'. Примечание: если STRICT опущен, по умолчанию используется 'no strict;'.
Некоторые моменты, на которые следует обратить внимание:
Если разрешён оператор entereval, код может использовать eval "..." для 'скрытия' кода, который может использовать запрещённые операторы. Это не является большой проблемой, поскольку при попытке выполнения кода eval он потерпит неудачу, так как маска opmask всё ещё действует. Однако этот метод позволит хитрому и, возможно, вредоносному коду 'ощупывать' границы возможного.
Любой строковый eval, который выполняется кодом, выполняющимся в отсеке, или кодом, вызываемым из кода, выполняющегося в отсеке, будет eval'd в пространстве имён отсека. Это потенциально серьёзная проблема.
Рассмотрим функцию foo() в пакете pkg, скомпилированную вне отсека, но разделенную с ним. Предположим, что отсек имеет корневой пакет под названием 'Root'. Если foo() содержит оператор eval, такой как eval '$foo = 1', то обычно $pkg::foo будет установлено в 1. Если foo() вызывается из отсека (любым способом), то вместо установки $pkg::foo, eval фактически установит $Root::pkg::foo.
Это легко продемонстрировать с помощью модуля, такого как Socket, который использует eval "..." в рамках функции AUTOLOAD. Вы можете 'use' модуль вне отсека и разделить (автозагружаемую) функцию с отсеком. Если автозагрузка инициируется кодом в отсеке или любым кодом, вызываемым любым способом из отсека, тогда eval в функции AUTOLOAD модуля Socket происходит в пространстве имён отсека. Любые переменные, созданные или используемые eval'd кодом, теперь находятся под контролем кода в отсеке.
Аналогичный эффект имеет место для всех поисков символов во время выполнения в коде, вызываемом из отсека, но не скомпилированном в нём.
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.34.0/Safe