Back to Blog

Jinja2テンプレートのエスケープについて解説!

flask

2024年7月8日

こんにちは!

今回の記事では、Jinja2テンプレートのエスケープの方法を解説します。

Jinja2はFlaskで使われるデフォルトのテンプレートエンジンです。

Jinja2の中で、重要な機能の一つに「エスケープ」があります。

この記事では、FlaskとJinja2におけるエスケープ機能について詳しく解説し、安全なWebアプリケーションを構築するためのベストプラクティスを紹介します。

エスケープとは?

そもそも、エスケープとは何なのでしょうか?

まずはエスケープ処理の概要について解説します。

エスケープ(escaping)とは、特定の文字列を別の形式に変換することで、特定のコンテキストでその文字列が意図しない動作をするのを防ぐ手法です。

例えば、「こんにちは私の名前は”太郎”です。」と表示したいとします。

この時、以下のように実行した場合、どのようになるでしょうか?

print("こんにちは私の名前は"太郎"です。")

この場合、”こんにちは私の名前は”までが、区切りだと判断されて、正常に出力されません。プログラミング言語の中では、”(ダブルクオーテーション)は文字列を区切るために使われます。

そこで、エスケープ処理を行い、”(ダブルクオーテーション)を文字列として扱います。

print("こんにちは私の名前は\"太郎\"です。")

Flaskは自動エスケープが有効になっている

FlaskでHTMLタグを含む文字列を表示させる場合、Jinja2のテンプレートエンジンが自動エスケープを行うため、そのままではエスケープされてしまいます。

自動エスケープを確認してみよう

例えば、HTML上に「test」という内容を表示したいとします。以下のように、html_contentを定義して、Jinja2テンプレートに渡します。

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    html_content = "<p>test</p>"
    return render_template('index.html', html_content=html_content)

if __name__ == '__main__':
    app.run(debug=True)

テンプレートである、index.htmlはこちらです。

<!doctype html>
<html>
<body>
    {{ html_content }}
</body>
</html>

通常通り、設定すると、html_contentはどのように表示されるでしょうか?

この場合、<p>タグはエスケープされるため、<p>test</p>とブラウザ上に表示されます。

これは、Flaskが自動でエスケープ処理をしてくれるためです。

エスケープを無効にする

エスケープを無効にするには、| safeフィルタもしくは{% autoescape false %}タグを使用します。

index.htmlを以下のように変更します。

<!doctype html>
<html>
<body>
    {{ html_content | safe }}
</body>
</html>

このようにテンプレートを設定すると、ブラウザには、testと表示されます。

これは、自動エスケープを無効にすることで、html_content = “<p>test</p>”の<p>がHTMLのタグと判定されます。

| safeフィルタを使用することで、エスケープ処理を無効化し、HTMLタグを含む文字列をそのまま表示することができます。

なぜ自動エスケープが有効になっているのか?

なぜ自動エスケープが有効になっているのでしょうか?

これは、クロスサイトスクリプティング攻撃を防ぐためです。

クロスサイトスクリプティング(XSS攻撃は、Webアプリケーションのフォームに実行可能な文字列を挿入し、Webアプリケーションの利用者の環境で悪意のある操作を実行する攻撃です。

クロスサイトスクリプティングを試してみる

クロスサイトスクリプティングがどのように動作するのかを試してみましょう。エスケープ処理の重要性を理解することができます。

まず、app.pyを以下のように設定します。

from flask import Flask, render_template, request

app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def index():
    user_input = ""
    if request.method == 'POST':
        user_input = request.form['user_input']
    return render_template('index.html', user_input=user_input)

if __name__ == '__main__':
    app.run(debug=True)

次にテンプレートを以下のように変更します。

<!doctype html>
<html>
<body>
    <h1>Enter some text:</h1>
    <form method="post">
        <textarea name="user_input" rows="4" cols="50"></textarea><br>
        <input type="submit" value="Submit">
    </form>
    <h2>User Input:</h2>
    {% autoescape false %}
        <div>{{ user_input }}</div>
    {% endautoescape %}
</body>
</html>

特定のブロック内で自動エスケープを無効にするには、{% autoescape false %}タグを使用します

Flaskを起動してWebブラウザでアクセスすると、以下のようなフォームが表示されます。

動作確認

フォームに以下のようなスクリプトを記述します。

<script>alert('XSS');</script>

フォームのsubmitボタンをクリックすると、javascriptが実行されます。

このように、エスケープ処理を無効にすると、予期せぬスクリプトを実行される可能性があります。エスケープ処理の無効化は慎重に行いましょう。

まとめ

今回の記事では、Jinja2テンプレートのエスケープについて解説しました。

Flaskでは、エスケープ処理が自動で有効になっています。この場合、HTMLタグなどはそのまま表示されます。HTMLとして表示させたい場合は、エスケープ処理を無効にする必要があります。

しかし、エスケープ処理を無効にするとクロスサイトスクリプティング攻撃をされる恐れがあるので、慎重に設定を行いましょう。

このようなセキュリティに関する知識もWeb開発には重要です。今回の記事が何かのお役に立てれば幸いです。

ここまでお読みいただきありがとうございました。

Webの知識を作りながら学ぶ
Flask WebAcademy

SNSアプリやチャットアプリ、さまざまなアプリを作りながら学べる実践的なカリキュラム

詳細を見る