### 導航
- [索引](# "總目錄")
- [下一頁](# "Flask 中的 Unicode") |
- [上一頁](# "HTML/XHTML 常見問題") |
- [Flask 0.10.1 文檔](#) ?
# 安全注意事項
Web 應用通常面臨所有種類的安全問題,并且很難把所有事做的正確。 Flask 試圖為你解決這些事情中的一些,但你仍需要關心更多的問題。
### 跨站腳本攻擊(XSS)
跨站腳本攻擊的概念是在一個網站的上下文中注入任意的 HTML (以及附帶的JavaScript )。開發者需要正確地轉義文本,使其不能包含任意 HTML 標簽來避免這種攻擊。更多的信息請閱讀維基百科上關于 [Cross-Site Scripting](http://en.wikipedia.org/wiki/Cross-site_scripting) [http://en.wikipedia.org/wiki/Cross-site_scripting] 的文章。
Flask 配置 Jinja2 自動轉義所有值,除非顯式地指明不轉義。這就排除了模板導致的所有 XSS 問題,但是你仍需要在其它的地方小心:
- 生成 HTML 而不使用 Jinja2
- 在用戶提交的數據上調用了 [Markup](# "flask.Markup")
- 發送上傳的 HTML 文件,永遠不要這么做,使用Content-Disposition: attachment 標頭來避免這個問題
- 發送上傳的文本文件。一些瀏覽器使用基于開頭幾個字節的 content-type猜測,所以用戶可能欺騙瀏覽器執行 HTML
另一件非常重要的事情是未用引號包裹的屬性。雖然 Jinja2 可以通過轉義 HTML來保護你免受 XSS 問題,仍有一種情況,它不能保護你: 屬性注入的 XSS 。為了應對這種攻擊媒介,確保當在屬性中使用 Jinja 表達式時,始終用單引號或雙引號包裹屬性:
~~~
<a href="{{ href }}">the text</a>
~~~
為什么這是必要的?因為如果你不這么做,攻擊者可以容易地注入自制的JavaScript 處理器。譬如一個攻擊者可以注入這段 HTML+JavaScript:
~~~
onmouseover=alert(document.cookie)
~~~
當用戶鼠標經過這個鏈接, 會在警告窗口里把 cookie 顯示給用戶。一個精明的攻擊者可能也會執行其它的 JavaScript 代碼,而不是把 cookie 顯示給用戶。同 CSS 注入聯系在一起,攻擊者甚至使得元素填滿整個頁面,這樣用戶鼠標在頁面上的任何地方都會觸發攻擊。
### 跨站請求偽造(CSRF)
另一個大問題是 CSRF 。這是一個非常復雜的話題,我不會在此詳細介紹,而只會提及 CSRF 是什么和理論上如何避免它。
如果你的驗證信息存儲在 cookie 中,你有隱式的狀態管理。“已登入”狀態由一個cookie 控制,并且這個 cookie 在每個頁面的請求中都會發送。不幸的是,在第三方站點觸發的請求中也會發送這個 cookie 。如果你不注意這點,一些人可能會通過社會工程學來誘導你應用的用戶在他們不知道的情況下做一些蠢事。
比如你有一個指定的 URL ,當你發送 POST 請求時會刪除一個用戶的資料(比如http://example.com/user/delete 。如果一個攻擊者現在創造一個頁面來用JavaScript 發送這個 post 請求,他們只是誘騙一些用戶加載那個頁面,而他們的資料最終會被刪除。
想象你在運行 Facebook ,有數以百萬計的并發用戶,并且某人放出一些小貓圖片的鏈接。當用戶訪問那個頁面欣賞毛茸茸的貓的圖片時,他們的資料就被刪除。
你怎樣才能阻止這呢?基本上,對于每個修改服務器上內容的請求,你應該使用一次性令牌,并存儲在 cookie 里, **并且** 在發送表單數據的同時附上它。在服務器再次接收數據之后,你要比較兩個令牌,并確保它們相等。
為什么 Flask 沒有為你這么做?理想情況下,這應該是表單驗證框架做的事,而Flask 中并不存在表單驗證。
### JSON 安全
ECMAScript 5 的變更
從 ECMAScript 5 開始,常量的行為變化了。現在它們不由 Array 或其它的構造函數構造,而是由 Array 的內建構造函數構造,關閉了這個特殊的攻擊媒介。
JSON 本身是一種高級序列化格式,所以它幾乎沒有什么可以導致安全問題,對嗎?你不能聲明導致問題的遞歸結構,唯一可能導致破壞的就是在接受者角度上,非常大的響應可以導致某種意義上的拒絕服務攻擊。
然而有一個陷阱。由于瀏覽器在 CSRF 問題上工作的方式, JSON 也不能幸免。幸運的是, JavaScript 規范中有一個怪異的部分可以用于簡易地解決這一問題。 Flask通過避免你做危險的事情上為你解決了一些。不幸的是,只有在jsonify() 中有這樣的保護,所以如果你用其它方法生成 JSON 仍然有風險。
那么,問題是什么,并且怎樣避免?問題是 JSON 中數組是一等公民。想象你在一個 JSON 請求中發送下面的數據。比如 JavaScript 實現的用戶界面的一部分,導出你所有朋友的名字和郵件地址。并不罕見:
~~~
[
{"username": "admin",
"email": "admin@localhost"}
]
~~~
這當然只在你登入的時候,且只為你這么做。而且,它對一個特定 URL 上的所有GET 請求都這么做。比如請求的 URL 是http://example.com/api/get_friends.json
那么如果一個聰明的黑客把這個嵌入到他自己的網站上,并用社會工程學使得受害者訪問他的網站,會發生什么:
~~~
<script type=text/javascript>
var captured = [];
var oldArray = Array;
function Array() {
var obj = this, id = 0, capture = function(value) {
obj.__defineSetter__(id++, capture);
if (value)
captured.push(value);
};
capture();
}
</script>
<script type=text/javascript
src=http://example.com/api/get_friends.json></script>
<script type=text/javascript>
Array = oldArray;
// now we have all the data in the captured array.
</script>
~~~
如果你懂得一些 JavaScript 的內部工作機制,你會知道給構造函數打補丁和為setter 注冊回調是可能的。一個攻擊者可以利用這點(像上面一樣上)來獲取所有你導出的 JSON 文件中的數據。如果在 script 標簽中定義了內容類型是text/javascript ,瀏覽器會完全忽略 application/json 的mimetype ,而把其作為 JavaScript 來求值。因為頂層數組元素是允許的(雖然沒用)且我們在自己的構造函數中掛鉤,在這個頁面載入后, JSON 響應中的數據會出現在 captured 數組中。
因為在 JavaScript 中對象字面量( {...} )處于頂層是一個語法錯誤,攻擊者可能不只是用 script 標簽加載數據并請求一個外部的 URL 。所以, Flask所做的只是在使用 jsonify() 時允許對象作為頂層元素。確保使用普通的 JSON 生成函數時也這么做。
? 版權所有 2013, Armin Ronacher.
- 歡迎使用 Flask
- 前言
- 給有經驗程序員的前言
- 安裝
- 快速入門
- 教程
- 介紹 Flaskr
- 步驟 0: 創建文件夾
- 步驟 1: 數據庫模式
- 步驟 2: 應用設置代碼
- 步驟 3: 創建數據庫
- 步驟 4: 請求數據庫連接
- 步驟 5: 視圖函數
- 步驟 6: 模板
- 步驟 7: 添加樣式
- 福利: 應用測試
- 模板
- 測試 Flask 應用
- 記錄應用錯誤
- 配置處理
- 信號
- 即插視圖
- 應用上下文
- 請求上下文
- 用藍圖實現模塊化的應用
- Flask 擴展
- 與 Shell 共舞
- Flask 代碼模式
- 大型應用
- 應用程序的工廠函數
- 應用調度
- 使用 URL 處理器
- 部署和分發
- 使用 Fabric 部署
- 在 Flask 中使用 SQLite 3
- 在 Flask 中使用 SQLAlchemy
- 上傳文件
- 緩存
- 視圖裝飾器
- 使用 WTForms 進行表單驗證
- 模板繼承
- 消息閃現
- 用 jQuery 實現 Ajax
- 自定義錯誤頁面
- 延遲加載視圖
- 在 Flask 中使用 MongoKit
- 添加 Favicon
- 數據流
- 延遲請求回調
- 添加 HTTP Method Overrides
- 請求內容校驗碼
- 基于 Celery 的后臺任務
- 部署選擇
- mod_wsgi (Apache)
- 獨立 WSGI 容器
- uWSGI
- FastCGI
- CGI
- 聚沙成塔
- API
- JSON 支持
- Flask 中的設計決策
- HTML/XHTML 常見問題
- 安全注意事項
- Flask 中的 Unicode
- Flask 擴展開發
- Pocoo 風格指引
- Python 3 支持
- 升級到最新版本
- Flask Changelog
- 許可證
- 術語表