编写上下文处理器
编写自己的上下文处理器指南
上下文处理器有一个非常简单的接口:它只是一个Python函数,它接受一个参数,一个HttpRequest对象,并返回一个被添加到模板上下文的字典。 每个情境处理器必须返回一个字典。 这里有一些关于你自己的技巧:
-
使每个上下文处理器对可能的最小功能子集负责。 使用多个处理器很容易,因此您可以将功能分解为逻辑块以供将来重用。
-
请记住,TEMPLATE_CONTEXT_PROCESSORS中的任何上下文处理器都将在由该设置文件支持的每个模板中都可用,因此请尝试选择不大可能与模板可能独立使用的变量名称冲突的变量名称。 由于变量名是区分大小写的,所以对处理器提供的变量使用全部大写字母并不是一个坏主意。
-
自定义上下文处理器可以在你的代码库中的任何地方。 所有的Django关心的是你的自定义上下文处理器是由TEMPLATES设置中的'context_processors'选项指定的,或者如果你直接使用Engine的context_processors参数。 有了这个说法,约定就是把它们保存在你的应用程序或项目中的一个名为context_processors.py的文件中。
自动HTML转义
从模板生成HTML时,总是存在一个变量将包含影响生成的HTML的字符的风险。 例如,考虑这个模板片段:
Hello, {{ name }}.
起初,这似乎是一个无害的方式来显示用户的名字,但考虑如果用户输入他的名字会发生什么,如下所示:
<script>alert('hello')</script>
有了这个名字值,这个模板会被渲染为:
Hello, <script>alert('hello')</script>
这意味着浏览器会弹出一个JavaScript警告框! 同样,如果名称包含一个“<”符号,像这样?
<b>username
这将导致这样的渲染模板:
Hello, <b>username
...这反过来又会导致网页的其余部分被加粗! 显然,用户提交的数据不应该盲目地被信任,并直接插入到您的网页中,因为恶意用户可能会使用这种漏洞做可能的坏事。
这种类型的安全漏洞被称为跨站点脚本(XSS)攻击。 (有关安全性的更多信息,请参阅第19章)。 为了避免这个问题,你有两个选择:
-
您可以确保通过转义筛选器运行每个不受信任的变量,转换筛选器将可能有害的HTML字符转换为无害的字符。 这是Django头几年的默认解决方案,但问题在于,它将开发人员/模板创作者的责任放在你身上,以确保你逃避一切。 忘记逃避数据很容易。
-
你可以利用Django的自动HTML转义。 本节的其余部分将介绍自动转义的工作原理。
默认情况下,在Django中,每个模板都会自动转义每个变量标签的输出。 具体来说,这五个字符是逃脱的:
- <转换为&lt;
- ‘>’被转换为&gt;
- '(单引号)转换为“
- “(双引号)转换为”
- &被转换为&amp;
我们再一次强调,这种行为默认是开启的。 如果您使用的是Django的模板系统,那么您将受到保护。
如何关闭它
如果您不希望在每个站点,每个模板级别或每个变量级别上自动转义数据,则可以通过多种方法关闭数据。 你为什么要把它关掉? 因为有时,模板变量包含的数据,你打算被呈现为原始的HTML,在这种情况下,你不希望他们的内容被转义。
例如,您可能会在数据库中存储一个受信任的HTML块,并希望将其直接嵌入到您的模板中。 或者,您可能正在使用Django的模板系统来生成不是HTML的文本 - 比如电子邮件。
对于个体变量
要禁用单个变量的自动转义,请使用安全筛选器:
This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}
认为安全是避免进一步转义的安全速度,或者可以安全地解释为HTML。 在这个例子中,如果数据包含'<b>',输出将是:
This will be escaped: <b>
This will not be escaped: <b>
对于模板块
要控制模板的自动转义,请将模板(或模板的特定部分)包装在autoescape标记中,如下所示:
{% autoescape off %}
Hello {{ name }}
{% endautoescape %}
autoescape标签将其作为参数打开或关闭。 有时,您可能会强制自动转义,否则将被禁用。 这是一个示例模板:
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 %}
自动转义标签将其作用扩展到扩展当前标签的模板以及通过包含标签包含的模板,就像所有块标签一样。 例如:
# 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方面的开发人员(编写视图和自定义过滤器的人)需要考虑数据不应该被转义的情况,并适当地标记数据,以便在模板中工作。
如果您创建的模板可能在您不确定是否启用了自动转义的情况下使用,则可以将转义筛选器添加到任何需要转义的变量。 打开自动转义时,转义过滤器不会有双转义数据的危险 - 转义过滤器不会影响自动转义的变量。
自动转义过滤参数中的字符串文字
正如我们前面提到的,过滤器参数可以是字符串:
{{ data|default:"This is a string literal." }}
所有字符串文字都插入,没有任何自动转义到模板 - 它们的行为就好像它们都通过了安全过滤器。 这背后的原因是模板作者控制了字符串文字的内容,因此他们可以确保在写入模板时正确地转义文本。
这意味着你会写
{{ data|default:"3 < 2" }}
而不是
{{ data|default:"3 < 2" }} <== Bad! Don't do this.
这并不影响来自变量本身的数据。 如有必要,变量的内容仍然会自动转义,因为它们超出了模板作者的控制范围。