HTML代碼自動轉義(auto-escaping)
當使用模板生成HTML代碼時,如果變量內容是一些影響HTML結果的字符時,那就挺危險的。
例如,模板內容如下:
Hello {{ name }}
當name的值為:
<script>alert('hello')</script>
渲染后的HTML結果就是:
Hello <script>alert('hello')</script>
以上的代碼運行的結果就是會讓瀏覽器彈出一個javascript的警告窗口。
同理,如果name的值為<b>hanks,那么結果中Hello以后的所有內容就會被字體加粗,因為
沒有寫</b>結束標記。
這種攻擊方式被稱為跨站腳本攻擊(Cross Site Scripting,CSS或者XSS),是一種站點應
用程序的安全漏洞攻擊,是代碼注入的一種。它允許惡意用戶將代碼注入到網頁上,其他用戶在
觀看網頁時就會受到影響。這類攻擊通常包含了HTML以及用戶端腳本語言。
以上定義來自Wiki:
很顯然,任何時候都不要相信用戶輸入的數據,都要使用防御性編程,為了避免上面的問題,你有兩個選擇:
1. 使用模板中的filter功能,Django有提供一個escape filter,可以用來過濾掉所有你不信任的變量,不過
需要在每一個變量后使用,這樣很容易會漏掉對某個變量使用escape.
2. 使用Django的模板自動轉義功能。其實是默認開啟的
Django默認轉義每一個變量的內容,尤其是下面5個字符:
- < 被轉義成 <
- > 被轉義成 >
- ' 被轉義成 '
- " 被轉義成 "
- & 被轉義成 &
上面中的分號也是轉義后的一部分。
例如網頁內容:
頁面源代碼為:
可以看到,轉義只對變量的內容進行使用,模板本身的HTML代碼不會被轉義。
如何關閉這個功能?
為什么呢,有可能你有時就是想讓變量的內容渲染成原始的HTML代碼,所以不想被轉義。
比如你想讓template系統產生文本內容而不是HTML,就像email信息一樣。
Django提供了三種方式關閉自動轉義:變量級別,模板級別和站點級別。
1. 變量級別
使用safe這個過濾器對每一個變量進行禁用自動轉義
This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}
效果如下:
網頁內容
頁面源代碼
上面之所以沒有顯示出來,是HTML語法錯誤,這就是不轉義的下場。。。
2. 模板級別
在模板中使用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 %}
同時,autoescape標簽的影響具有繼承性,可以從父模板影響到子模板。
# 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>,
將不會被轉義。
需要注意的是,模板的作者不用去擔心自動轉義的使用。更多的python端的
開發人員需要考慮哪些數據需要去轉義,合理去使用這些數據。
如果你創建了一個模板,而且不太清楚所應用的環境是否開啟了自動轉義功能。那就在所有
的變量上加上escape過濾器,escape過濾器不會對已經escape的內容產生影響。
對於filter過濾器中參數的自動轉義
Django中的自動轉義功能不會對filter中的參數已作用,也就是說,最好在filter的參數中,自己
寫成轉義后的代碼,比如這種情況你應該手寫成
{{ data|default:"3 < 2" }}
而不是
{{ data|default:"3 < 2" }}
這是為了安全起見。
