サンドボックス

Jinja のサンドボックスは、信頼できないテンプレートをレンダリングするために使用できます。属性、メソッド呼び出し、演算子、データ構造の変更、および文字列フォーマットへのアクセスをインターセプトして禁止することができます。

>>> 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やメモリなどのリソースに制限を設けてアプリケーションを実行する必要があります。

Jinja はテキストのみをレンダリングし、たとえば JavaScript コードは理解しません。レンダリングされたテンプレートの使用方法によっては、出力を制限するために追加の後処理が必要になる場合があります。

テンプレートに関連するデータのみを渡してください。グローバルデータや、副作用のあるメソッドを持つオブジェクトを渡さないでください。デフォルトでは、サンドボックスはプライベート属性と内部属性へのアクセスを阻止します。 is_safe_attribute() をオーバーライドして、属性アクセスをさらに制限できます。メソッドを unsafe() でデコレートして、オブジェクトをデータとして渡す際にテンプレートからの呼び出しを防止します。ImmutableSandboxedEnvironment を使用して、リストと辞書の変更を防ぎます。

API

class jinja2.sandbox.SandboxedEnvironment([options])

サンドボックス化された環境です。通常の環境と同様に動作しますが、コンパイラにサンドボックス化されたコードを生成するように指示します。さらに、この環境のサブクラスは、ランタイムがアクセスできる属性または関数を指示するメソッドをオーバーライドする場合があります。

テンプレートが安全でないコードにアクセスしようとすると、SecurityError が発生します。ただし、レンダリング中に他の例外が発生することもあるため、呼び出し側はすべての例外がキャッチされるようにする必要があります。

パラメータ:
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によって指定されます。

次の二項演算子はインターセプト可能です://%+*-/、および**

演算子テーブルからのデフォルトの操作は、組み込み関数に対応します。インターセプトされた呼び出しはネイティブの演算子呼び出しよりも常に遅いため、関心のあるものだけをインターセプトするようにしてください。

変更ログ

バージョン 2.6 で追加されました。

intercepted_unops: FrozenSet[str] = frozenset({})

インターセプトされるべき単項演算子のセットです。このセットに追加された各演算子(デフォルトでは空)は、演算を実行するcall_unop()メソッドに委任されます。デフォルトの演算子コールバックはunop_tableによって指定されます。

次の単項演算子はインターセプト可能です:+-

演算子テーブルからのデフォルトの操作は、組み込み関数に対応します。インターセプトされた呼び出しはネイティブの演算子呼び出しよりも常に遅いため、関心のあるものだけをインターセプトするようにしてください。

変更ログ

バージョン 2.6 で追加されました。

is_safe_attribute(obj, attr, value)

サンドボックス化された環境は、オブジェクトの属性へのアクセスが安全かどうかを確認するためにこのメソッドを呼び出します。デフォルトでは、アンダースコアで始まるすべての属性は、is_internal_attribute() 関数によって返される内部 Python オブジェクトの特別な属性と同様に、プライベートと見なされます。

パラメータ:
戻り値:

bool

is_safe_callable(obj)

オブジェクトが安全に呼び出し可能かどうかを確認します。デフォルトでは、unsafe()でデコレートされていない限り、呼び出し可能オブジェクトは安全と見なされます。

これは、func.alters_data = Trueを設定するというDjangoの慣習も認識します。

パラメータ:

obj (Any)

戻り値:

bool

call_binop(context, operator, left, right)

インターセプトされた二項演算子の呼び出し(intercepted_binops())の場合、この関数は組み込み演算子の代わりに実行されます。これは、特定の演算子の動作を微調整するために使用できます。

変更ログ

バージョン 2.6 で追加されました。

パラメータ:
戻り値:

任意

call_unop(context, operator, arg)

インターセプトされた単項演算子の呼び出し(intercepted_unops())の場合、この関数は組み込み演算子の代わりに実行されます。これは、特定の演算子の動作を微調整するために使用できます。

変更ログ

バージョン 2.6 で追加されました。

パラメータ:
戻り値:

任意

class jinja2.sandbox.ImmutableSandboxedEnvironment([options])

通常のSandboxedEnvironmentとまったく同じように機能しますが、modifies_known_mutable()関数を使用して、組み込みの変更可能なオブジェクトlistsetdictに対する変更を許可しません。

パラメータ:
exception jinja2.sandbox.SecurityError(message=None)

サンドボックスが有効になっている場合に、テンプレートがセキュリティ上の問題のある操作を試行すると発生します。

パラメータ:

message (str | None)

戻り値:

なし

jinja2.sandbox.unsafe(f)

関数またはメソッドを安全でないものとしてマークします。

パラメータ:

f (F)

戻り値:

F

jinja2.sandbox.is_internal_attribute(obj, attr)

指定された属性が内部Python属性かどうかをテストします。たとえば、この関数はPythonオブジェクトのfunc_code属性に対してTrueを返します。これは、環境メソッドis_safe_attribute()をオーバーライドする場合に役立ちます。

>>> from jinja2.sandbox import is_internal_attribute
>>> is_internal_attribute(str, "mro")
True
>>> is_internal_attribute(str, "upper")
False
パラメータ:
戻り値:

bool

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
パラメータ:
戻り値:

bool

演算子のインターセプト

パフォーマンスのために、Jinjaはコンパイル時に演算子を直接出力します。これは、デフォルトではSandboxEnvironment.callをオーバーライドすることで演算子の動作をインターセプトすることができないことを意味します。なぜなら、演算子の特殊メソッドはPythonインタープリターによって処理され、演算子の使用方法によっては正確に1つのメソッドに対応しない可能性があるからです。

サンドボックスは、代わりに特定の演算子をインターセプトする関数をコンパイラに出力するように指示できます。SandboxedEnvironment.intercepted_binopsSandboxedEnvironment.intercepted_unopsをインターセプトする演算子記号でオーバーライドします。コンパイラは、これらの記号をSandboxedEnvironment.call_binop()SandboxedEnvironment.call_unop()への呼び出しに置き換えます。これらのメソッドのデフォルトの実装では、SandboxedEnvironment.binop_tableSandboxedEnvironment.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)