沙箱¶
JJJA沙箱可用于呈现不受信任的模板。对属性、方法调用、运算符、变异数据结构和字符串格式的访问可以被拦截和禁止。
>>> from jinja2.sandbox import SandboxedEnvironment
>>> env = SandboxedEnvironment()
>>> func = lambda: "Hello, Sandbox!"
>>> env.from_string("{{ func() }}").render(func=func)
'Hello, Sandbox!'
>>> env.from_string("{{ func.__code__.co_code }}").render(func=func)
Traceback (most recent call last):
...
SecurityError: access to attribute '__code__' of 'function' object is unsafe.
例如,沙箱环境可用于允许内部报告系统的用户创建自定义电子邮件。您将记录模板中可用的数据,然后用户将使用该信息编写模板。您的代码将生成报表数据,并将其传递给用户的沙箱模板以呈现。
安全注意事项¶
沙箱本身并不是完美安全的解决方案。在使用沙箱时请牢记这些事项。
模板在编译或呈现时仍会引发错误。您的代码应该尝试捕获错误,而不是崩溃。
可以构造一个相对较小的模板来呈现非常大量的输出,这可能对应于CPU或内存的高使用率。您应该在运行应用程序时限制CPU和内存等资源,以缓解这一问题。
JJJA只呈现文本,它不理解,例如,JavaScript代码。根据渲染模板的使用方式,您可能需要执行其他后处理以限制输出。
只传递与模板相关的数据。避免传递具有副作用的方法的全局数据或对象。默认情况下,沙箱阻止私有和内部属性访问。您可以覆盖 is_safe_attribute()
以进一步限制属性访问。用来修饰方法 unsafe()
以防止在将对象作为数据传递时从模板调用它们。使用 ImmutableSandboxedEnvironment
要防止修改列表和词典,请执行以下操作。
API¶
- class jinja2.sandbox.SandboxedEnvironment([options])¶
沙盒环境。它的工作方式与常规环境类似,但它告诉编译器生成沙盒代码。此外,此环境的子类可能会重写那些告诉运行时哪些属性或函数可以安全访问的方法。
如果模板试图访问不安全的代码A
SecurityError
提高了。但是,在呈现期间也可能发生其他异常,因此调用方必须确保捕获所有异常。- call_binop(context, operator, left, right)¶
对于截获的二进制运算符调用 (
intercepted_binops()
)执行此函数而不是内置运算符。这可以用来微调某些运算符的行为。Changelog
在 2.6 版本加入.
- call_unop(context, operator, arg)¶
对于截获的一元运算符调用 (
intercepted_unops()
)执行此函数而不是内置运算符。这可以用来微调某些运算符的行为。Changelog
在 2.6 版本加入.
- default_binop_table: Dict[str, Callable[[Any, Any], Any]] = {'%': <built-in function mod>, '*': <built-in function mul>, '**': <built-in function pow>, '+': <built-in function add>, '-': <built-in function sub>, '/': <built-in function truediv>, '//': <built-in function floordiv>}¶
二进制运算符的默认回调表。此副本在沙盒环境的每个实例上都可用,如下所示
binop_table
- default_unop_table: Dict[str, Callable[[Any], Any]] = {'+': <built-in function pos>, '-': <built-in function neg>}¶
一元运算符的默认回调表。此副本在沙盒环境的每个实例上都可用
unop_table
- intercepted_binops: FrozenSet[str] = frozenset({})¶
应该截取的一组二进制运算符。添加到此集合中的每个运算符(默认为空)都委托给
call_binop()
将执行运算符的方法。默认的运算符回调由binop_table
.以下二进制运算符是可拦截的:
//
,%
,+
,*
,-
,/
, and `` **operator表的默认操作对应于内置函数。截获的调用总是比本机运算符调用慢,因此请确保只截获您感兴趣的调用。
Changelog
在 2.6 版本加入.
- intercepted_unops: FrozenSet[str] = frozenset({})¶
应截取的一元运算符集。添加到此集合中的每个运算符(默认为空)都委托给
call_unop()
将执行运算符的方法。默认的运算符回调由unop_table
.以下一元运算符是可拦截的:
+
,-
operator表的默认操作对应于内置函数。截获的调用总是比本机运算符调用慢,因此请确保只截获您感兴趣的调用。
Changelog
在 2.6 版本加入.
- is_safe_attribute(obj, attr, value)¶
沙盒环境将调用此方法以检查对象的属性是否可以安全访问。默认情况下,所有以下划线开头的属性都被视为私有属性,以及由
is_internal_attribute()
功能。
- class jinja2.sandbox.ImmutableSandboxedEnvironment([options])¶
和普通的一样 SandboxedEnvironment 但不允许对内置可变对象进行修改 list , set 和 dict 通过使用
modifies_known_mutable()
功能。
- exception jinja2.sandbox.SecurityError(message=None)¶
如果模板尝试执行不安全的操作(如果启用了沙盒),则引发。
- 参数:
message (str | None) --
- 返回类型:
None
- jinja2.sandbox.unsafe(f)¶
将函数或方法标记为不安全。
- 参数:
f (F) --
- 返回类型:
F
- jinja2.sandbox.is_internal_attribute(obj, attr)¶
测试给定的属性是否为内部python属性。例如,此函数返回 True 对于 func_code python对象的属性。如果环境方法
is_safe_attribute()
被重写。>>> from jinja2.sandbox import is_internal_attribute >>> is_internal_attribute(str, "mro") True >>> is_internal_attribute(str, "upper") False
- jinja2.sandbox.modifies_known_mutable(obj, attr)¶
此函数检查内置可变对象(list、dict、set或deque)上的属性或相应的ABC是否会在调用时修改它。
>>> modifies_known_mutable({}, "clear") True >>> modifies_known_mutable({}, "keys") False >>> modifies_known_mutable([], "append") True >>> modifies_known_mutable([], "index") False
如果使用不支持的对象调用,
False
返回。>>> modifies_known_mutable("foo", "upper") False
运算符拦截¶
为了提高性能,JJJA在编译时直接输出操作符。这意味着无法通过重写来拦截操作员行为 SandboxEnvironment.call
默认情况下,因为运算符特殊方法是由Python解释器处理的,根据运算符的使用,它可能并不只对应于一个方法。
沙箱可以指示编译器输出函数来截取某些操作符。超覆 SandboxedEnvironment.intercepted_binops
和 SandboxedEnvironment.intercepted_unops
带有您要截取的运算符符号。编译器将用调用 SandboxedEnvironment.call_binop()
和 SandboxedEnvironment.call_unop()
取而代之的是。这些方法的默认实现将使用 SandboxedEnvironment.binop_table
和 SandboxedEnvironment.unop_table
将运算符符号转换为 operator
功能。
例如,权力 (**
)操作员可以禁用:
from jinja2.sandbox import SandboxedEnvironment
class MyEnvironment(SandboxedEnvironment):
intercepted_binops = frozenset(["**"])
def call_binop(self, context, operator, left, right):
if operator == "**":
return self.undefined("The power (**) operator is unavailable.")
return super().call_binop(self, context, operator, left, right)