Django テンプレートから JavaScript にデータを渡す場面
Django で Web アプリケーションを開発していると、サーバーサイドで生成したデータをフロントエンドの JavaScript で利用したい場面が頻繁にあります。
たとえば、ユーザー情報や設定値、API のエンドポイント URL などをテンプレート経由で JavaScript に渡すケースです。
しかし、データの渡し方を誤ると XSS (クロスサイトスクリプティング) の脆弱性を生み出す危険があります。本記事では、Django が提供する json_script フィルターを使って安全にデータを渡す方法を解説します。
よくある実装とその問題点
Django テンプレートから JavaScript にデータを渡す方法として、以下のようにテンプレート変数を直接 JavaScript に埋め込むコードを見かけることがあります。
<script>
const userName = "{{ user_name }}";
const config = {{ config_json }};
</script>
この方法には重大な問題があります。もし user_name に悪意のある文字列 (たとえば <script> タグを含む値) が入っていた場合、ブラウザはそれを JavaScript として実行してしまいます。
Django のテンプレートエンジンはデフォルトで HTML エスケープを行いますが、これは HTML コンテキストでの対策であり、JavaScript の文字列リテラル内では十分に機能しません。safe フィルターや autoescape off を併用している場合はさらに危険です。
json_script フィルターを使った安全な実装
Django 2.1 以降で利用できる json_script テンプレートフィルターは、この問題を安全に解決します。
json_script は Python オブジェクトを JSON としてシリアライズし、XSS 対策が施された <script type="application/json"> タグとして出力します。
まず、ビューでコンテキストにデータを渡します。
class ExampleView(ViewMixin, TemplateView):
template_name = f"{AppConfig.name}/example.html"
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
context = {
"page_config": {
"api_endpoint": "/api/data/",
"max_items": 50,
"user_name": self.request.user.get_full_name(),
},
}
return super().get_context_data(**kwargs) | context
次に、テンプレートで json_script フィルターを使ってデータを埋め込みます。
{{ page_config|json_script:"page-config" }}
このフィルターは、以下のような HTML を自動生成します。
<script id="page-config" type="application/json">
{"api_endpoint": "/api/data/", "max_items": 50, "user_name": "..."}
</script>
type="application/json" が指定されているため、ブラウザはこのスクリプトタグの中身を JavaScript として実行しません。あくまでデータの格納場所として機能します。
JavaScript 側では、以下のようにデータを取得します。
const pageConfig = JSON.parse(document.getElementById("page-config").textContent);
console.log(pageConfig.api_endpoint);
console.log(pageConfig.max_items);
json_script フィルターは内部で < や >、& などの危険な文字を Unicode エスケープするため、悪意のある文字列が含まれていても安全に処理されます。
まとめ
Django テンプレートから JavaScript にデータを渡す際は、テンプレート変数を直接 JavaScript に埋め込むのではなく、json_script フィルターを使用しましょう。XSS 対策が自動的に適用されるうえ、辞書やリストなどの複雑なデータ構造もそのまま渡せるため、コードの見通しも良くなります。
Django 2.1 以降であれば追加のインストールなしで利用できるので、新規開発はもちろん、既存のテンプレート変数埋め込みを置き換えるリファクタリングにも活用してみてください。
コメント