安全性考量¶
Web 應用程式面臨許多類型的潛在安全問題,而且要完全正確地處理所有問題可能很困難,甚至一般來說很難知道「正確」是什麼。Flask 預設嘗試解決其中一些問題,但您可能需要自行處理其他部分。許多這些解決方案都是權衡取捨,並且將取決於每個應用程式的特定需求和威脅模型。許多託管平台可能會處理某些類型的問題,而無需 Flask 應用程式來處理。
資源使用¶
常見的攻擊類別是「阻斷服務」(DoS 或 DDoS)。這是一個非常廣泛的類別,不同的變體針對已部署應用程式中的不同層級。一般來說,會採取某些措施來增加處理每個請求所使用的處理時間或記憶體,以至於沒有足夠的資源來處理合法的請求。
Flask 提供了一些配置選項來處理資源使用。它們也可以在個別請求上設定,以僅自訂該請求。每個選項的文件中都有更詳細的說明。
MAX_CONTENT_LENGTH
或Request.max_content_length
控制將從請求中讀取多少資料。預設情況下未設定,但除非 WSGI 伺服器指示支援,否則它仍然會阻止真正無限的串流。MAX_FORM_MEMORY_SIZE
或Request.max_form_memory_size
控制任何非檔案multipart/form-data
欄位可以有多大。預設設定為 500kB。MAX_FORM_PARTS
或Request.max_form_parts
控制可以解析多少multipart/form-data
欄位。預設設定為 1000。結合預設的max_form_memory_size
,這表示表單最多將佔用 500MB 的記憶體。
無論這些設定如何,您也應該檢查您的作業系統、容器部署 (Docker 等)、WSGI 伺服器、HTTP 伺服器和託管平台提供的設定。它們通常有方法可以設定程序資源限制、逾時和其他檢查,而與 Flask 的配置方式無關。
跨站腳本攻擊 (XSS)¶
跨站腳本攻擊是指將任意 HTML(以及 JavaScript)注入網站上下文的概念。為了補救這個問題,開發人員必須正確地逸出文字,使其不能包含任意 HTML 標籤。有關這方面的更多資訊,請參閱 Wikipedia 上關於 跨站腳本攻擊 的文章。
Flask 配置 Jinja2 自動逸出所有值,除非明確告知不要這樣做。這應該排除所有在模板中引起的 XSS 問題,但仍然有一些其他地方您必須小心
在沒有 Jinja2 幫助的情況下產生 HTML
在使用者提交的資料上呼叫
Markup
從上傳的檔案傳送 HTML,永遠不要這樣做,使用
Content-Disposition: attachment
標頭來防止這個問題。從上傳的檔案傳送文字檔。某些瀏覽器使用基於前幾個位元組的內容類型猜測,因此使用者可能會欺騙瀏覽器執行 HTML。
另一件非常重要的事情是未加引號的屬性。雖然 Jinja2 可以透過逸出 HTML 來保護您免受 XSS 問題的影響,但有一件事它無法保護您:透過屬性注入進行 XSS。為了應對這種可能的攻擊途徑,當在屬性中使用 Jinja 表達式時,請務必始終使用雙引號或單引號引起來
<input value="{{ value }}">
為什麼這有必要?因為如果您不這樣做,攻擊者可以輕鬆注入自訂 JavaScript 處理常式。例如,攻擊者可以注入這段 HTML+JavaScript
onmouseover=alert(document.cookie)
當使用者隨後將滑鼠移動到輸入框上方時,cookie 將在警示視窗中呈現給使用者。但是,一個好的攻擊者可能不僅僅是向使用者顯示 cookie,還可能執行任何其他 JavaScript 程式碼。結合 CSS 注入,攻擊者甚至可以使元素填滿整個頁面,以便使用者只需將滑鼠放在頁面上的任何位置即可觸發攻擊。
有一類 XSS 問題是 Jinja 的逸出無法防禦的。a
標籤的 href
屬性可以包含 javascript:
URI,如果未正確保護,瀏覽器將在點擊時執行它。
<a href="{{ value }}">click here</a>
<a href="javascript:alert('unsafe');">click here</a>
為了防止這種情況,您需要設定 內容安全策略 (CSP) 回應標頭。
跨站請求偽造 (CSRF)¶
另一個大問題是 CSRF。這是一個非常複雜的主題,我不會在這裡詳細概述它,只是提及它是什麼以及如何在理論上預防它。
如果您的身份驗證資訊儲存在 cookie 中,您就有了隱式狀態管理。「已登入」的狀態由 cookie 控制,並且該 cookie 會隨每個頁面請求一起傳送。不幸的是,這包括由第三方網站觸發的請求。如果您不記住這一點,有些人可能會透過社交工程來欺騙您的應用程式使用者做出愚蠢的事情,而他們卻不知道。
假設您有一個特定的 URL,當您向其傳送 POST
請求時,將刪除使用者的個人資料(例如 http://example.com/user/delete
)。如果攻擊者現在建立一個頁面,該頁面使用一些 JavaScript 向該頁面傳送 POST 請求,他們只需要欺騙一些使用者載入該頁面,他們的個人資料最終將被刪除。
想像一下,如果您要運行 Facebook,擁有數百萬的並發使用者,並且有人發送了小貓圖片的連結。當使用者訪問該頁面時,他們的個人資料將在他們觀看毛茸茸的貓咪圖片時被刪除。
您如何預防這種情況?基本上,對於每個修改伺服器內容的請求,您都必須使用一次性權杖,並將其儲存在 cookie 中 以及 與表單資料一起傳輸。在伺服器上再次收到資料後,您必須比較這兩個權杖並確保它們相等。
為什麼 Flask 不為您執行此操作?這種情況發生的理想位置是表單驗證框架,而 Flask 中不存在該框架。
JSON 安全性¶
在 Flask 0.10 及更低版本中,jsonify()
不會將頂層陣列序列化為 JSON。這是因為 ECMAScript 4 中的一個安全漏洞。
ECMAScript 5 關閉了這個漏洞,因此只有極其老舊的瀏覽器仍然容易受到攻擊。所有這些瀏覽器都有 其他更嚴重的漏洞,因此這種行為被更改了,並且 jsonify()
現在支援序列化陣列。
安全標頭¶
瀏覽器識別各種回應標頭,以便控制安全性。我們建議您檢查以下每個標頭,以便在您的應用程式中使用。可以使用 Flask-Talisman 擴充功能來為您管理 HTTPS 和安全標頭。
HTTP 嚴格傳輸安全 (HSTS)¶
告訴瀏覽器將所有 HTTP 請求轉換為 HTTPS,防止中間人 (MITM) 攻擊。
response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
內容安全策略 (CSP)¶
告訴瀏覽器可以從哪裡載入各種資源類型。應盡可能使用此標頭,但需要一些工作來為您的網站定義正確的策略。一個非常嚴格的策略將是
response.headers['Content-Security-Policy'] = "default-src 'self'"
X-內容類型選項¶
強制瀏覽器遵守回應內容類型,而不是嘗試檢測它,這可能會被濫用以產生跨站腳本攻擊 (XSS)。
response.headers['X-Content-Type-Options'] = 'nosniff'
X-框架選項¶
防止外部網站將您的網站嵌入到 iframe
中。這可以防止一類攻擊,其中外部框架中的點擊可以隱形地轉換為點擊您頁面上的元素。這也稱為「點擊劫持」。
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
HTTP 公鑰釘選 (HPKP)¶
這告訴瀏覽器僅使用特定的憑證金鑰向伺服器進行身份驗證,以防止 MITM 攻擊。
警告
啟用此功能時請小心,因為如果您設定或升級金鑰不正確,則很難撤銷。
複製/貼上到終端機¶
隱藏字元 (例如退格字元 (\b
, ^H
)) 可能會導致文字在 HTML 中的呈現方式與 貼到終端機中 時的解讀方式不同。
例如,import y\bose\bm\bi\bt\be\b
在 HTML 中呈現為 import yosemite
,但退格鍵在貼到終端機中時會被應用,它會變成 import os
。
如果您希望使用者從您的網站複製和貼上不受信任的程式碼,例如從技術部落格上的使用者發布的評論中複製和貼上,請考慮應用額外的篩選,例如替換所有 \b
字元。
body = body.replace("\b", "")
大多數現代終端機都會在貼上時警告並移除隱藏字元,因此這並非絕對必要。也可以透過其他無法篩選的方式來製作危險的命令。根據您網站的使用案例,顯示關於複製程式碼的一般警告可能是好的。