HTML の自動エスケープ(Django テンプレート言語 — Django v1.0 documentation)

 単純なrelogです。探しづらいので。

HTML の自動エスケープ

Django 1.0 で新たに登場しました.

テンプレートから HTML を生成するときには、変数内の文字が HTML のレンダリング結果を壊してしまうというリスクが常に付きまといます。例えば、以下のようなテンプレートフラグメントを考えてみましょう:

こんにちは {{ name }} さん

一件、このコードはユーザ名を表示するだけの無害なものに見えますが、名前を以下のような値にすると、何が起こるでしょう:

<script>alert('hello')</script>

テンプレートは以下のようにレンダされます:

Hello, <script>alert('hello')</script>

その結果、 JavaScript の警告ボックスを表示できてしまいます!

同様に、名前に以下のようにして '<' 文字をいれると同でしょう:

<b>username

レンダ結果は以下のようになります:

Hello, <b>username

これで、以降のWebページは全部ボールド表示になってしまいます!

言うまでもなく、ユーザが入力したデータは盲目に信頼してはならず、直接 Web ページに挿入すべきでもありません。悪意のあるユーザはこの手の穴を使って悪さをするものです。こうしたセキュリティホールは、 クロスサイトスクリプティング (XSS) 攻撃と呼ばれています。

クロスサイトスクリプティングを防ぐには、二つの方法があります:

  • まず、信頼できない変数は全て (後述の) escape フィルタを通します。このフィルタは危険を及ぼす可能性のある HTML 文字を無害な文字に変換します。 Django では、初期の数年はこの方法をデフォルトとして採用していましたが、ユーザ、すなわち開発者やテンプレートの作者に、全てをエスケープするよう気配りする負荷をかけるという問題がありました。
  • もう一つは、自動 HTML エスケープを使うというものです。この節の後半では、自動エスケープのからくりについて述べています。

開発版の Django では、デフォルトの設定として、全てのテンプレートは変数タグを自動的にエスケープします。具体的には、以下の 5 つの文字がエスケープされます:

  • <"&lt;" に変換されます。
  • >"&gt;" に変換されます。
  • "'" (クオート) は ''' に変換されます。
  • '"' (二重クオート) は '&quot;' に変換されます。
  • "&""&amp;" に変換されます。

この動作はデフォルトで適用されていることを強調しておきます。Django テンプレートシステムを使っているかぎり、エスケープに関する問題からは守られているのです。

自動エスケープを切るには

サイト単位やテンプレート単位、変数単位でデータの自動エスケープを切るには、いくつかの方法があります。

どんなときに、自動エスケープを切る必要があるのでしょうか。テンプレート変数の中には、生の HTML としてレンダするように 意図された データがあります。そうした場合にはコンテンツがエスケープされてほしくはないでしょう。例えば、データベースに HTML を保存していて、テンプレートに直接埋め込みたい場合を考えてみましょう。また、 Django のテンプレートシステムを使って、 HTML 以外 のデータ、例えば電子メールメッセージなどを生成したい場合がそうです。

変数単位の制御

変数個々に自動エスケープを無効にするには、 safe フィルタを使います:

エスケープされる: {{ data }}
エスケープされない: {{ data|safe }}

safe という言葉を、 これ以上エスケープしないよう保護(safe)する とか、 HTML として解釈しても安全(safe)である という意味だと考えてください。例えば、 data'<b>' が入っていた場合、出力は以下のようになります:

エスケープされる: &lt;b&gt;
エスケープされない: <b>
テンプレートブロック単位の制御

テンプレートで自動エスケープを制御するには、テンプレート (またはテンプレートの一部) を、以下のように autoescape タグで囲みます:

{% autoescape off %}
    こんにちは {{ name }} さん
{% endautoescape %}

autoescape タグは、 on または off を引数にとります。テンプレートのある範囲を自動エスケープして、さらにその一部で自動エスケープを切りたい場合には、以下のように入れ子にできます:

Auto-escaping is on by default. Hello {{ name }}

{% autoescape off %}
    This will not be auto-escaped: {{ data }}.

    Nor this: {{ other_data }}
    {% autoescape on %}
        Auto-escaping applies again: {{ name }}
    {% endautoescape %}
{% endautoescape %}

自動エスケープのタグは、他のブロックタグと同様、タグを設定したテンプレートを継承している他のテンプレートや、 include で取り込んだテンプレートでも有効です。例えば:

# base.html

{% autoescape off %}
<h1>{% block title %}{% endblock %}</h1>
{% block content %}
{% endblock %}
{% endautoescape %}


# child.html

{% extends "base.html" %}
{% block title %}This & that{% endblock %}
{% block content %}{{ greeting }}{% endblock %}

自動エスケープがベースのテンプレートで無効化されているので、子テンプレートでも自動エスケープは無効化されます。結果として、 greeting 変数の値が <b>Hello!</b> の時には以下の HTML がレンダされます:

<h1>This & that</h1>
<b>Hello!</b>
注意点

一般に、テンプレートの作者は自動エスケープをあまり意識するべきではありません。 Python サイドの開発者 (ビューやカスタムフィルタ) の作者が、どのデータをエスケープすべきでないかを考え、データを適切にマークして、テンプレートにうまく表示されるようにすべきなのです。

テンプレートを作成していて、自動エスケープが有効な環境で使われるかどうか分からないような場合には、エスケープの必要な変数全てに escape フィルタを追加してください。自動エスケープがオンの場合、 escape フィルタがデータを 二重にエスケープする ような危険性はありません。 escape フィルタは変数の自動エスケープには影響しないのです。

文字列リテラルと自動エスケープ

先に説明したように、フィルタの引数は文字列であってもかまいません:

{{ data|default:"文字列リテラルだよ" }}

文字列リテラルは、自動エスケープ されずに テンプレート内に挿入されます。文字列リテラルは、 safe フィルタを通して渡されたかのように振る舞います。このような仕様になっているのは、テンプレートの作者が文字列リテラルがどのように処理されるかを制御でき、テンプレートを書くときにテキストが正しくエスケープされるよう配慮できるからです。

従って、以下のようにテンプレートを書いてください:

{{ data|default:"3 &lt; 2" }}

以下のようにはしないでください:

{{ data|default:"3 < 2" }}  <-- ダメ。こんな書き方をしてはいけません

引数にリテラルを使っても、変数自体に対するエスケープの挙動には影響しません。変数はテンプレート作者の制御の範囲外にあり、自動的にエスケープされます。

Django テンプレート言語 — Django v1.0 documentation

カテゴリー: 未分類 パーマリンク

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中