サンドボックス¶
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 オブジェクトの特別な属性と同様に、プライベートと見なされます。
- is_safe_callable(obj)¶
オブジェクトが安全に呼び出し可能かどうかを確認します。デフォルトでは、
unsafe()
でデコレートされていない限り、呼び出し可能オブジェクトは安全と見なされます。これは、
func.alters_data = True
を設定するというDjangoの慣習も認識します。
- call_binop(context, operator, left, right)¶
インターセプトされた二項演算子の呼び出し(
intercepted_binops()
)の場合、この関数は組み込み演算子の代わりに実行されます。これは、特定の演算子の動作を微調整するために使用できます。変更ログ
バージョン 2.6 で追加されました。
- class jinja2.sandbox.ImmutableSandboxedEnvironment([options])¶
通常の
SandboxedEnvironment
とまったく同じように機能しますが、modifies_known_mutable()
関数を使用して、組み込みの変更可能なオブジェクトlist
、set
、dict
に対する変更を許可しません。
- 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
- 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
演算子のインターセプト¶
パフォーマンスのために、Jinjaはコンパイル時に演算子を直接出力します。これは、デフォルトではSandboxEnvironment.call
をオーバーライドすることで演算子の動作をインターセプトすることができないことを意味します。なぜなら、演算子の特殊メソッドはPythonインタープリターによって処理され、演算子の使用方法によっては正確に1つのメソッドに対応しない可能性があるからです。
サンドボックスは、代わりに特定の演算子をインターセプトする関数をコンパイラに出力するように指示できます。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)