1. 項目概述為什么Flexx應用需要特別的安全關注最近在社區里看到不少朋友開始用Flexx來開發桌面應用尤其是那些想把Web應用打包成獨立桌面程序的項目。Flexx這個框架確實挺有意思它讓你能用純Python寫前端界面然后通過Web技術渲染最后還能打包成可執行文件。聽起來很美對吧但作為一個踩過不少坑的老碼農我得提醒你這種架構在帶來便利的同時也引入了一些獨特的安全風險很多從傳統Web開發轉過來的朋友很容易忽略。Flexx應用的安全和你平時寫的Django、Flask應用的安全側重點不太一樣。傳統的Web應用攻擊面主要集中在服務器端——SQL注入、XSS、CSRF這些防火墻、WAF還能幫上忙。但Flexx應用呢當它被打包成桌面應用后整個“后端”邏輯其實都跑在用戶的本地環境里。你的Python代碼、可能存在的敏感邏輯、甚至一些配置信息都暴露在用戶的機器上。這時候攻擊者不需要突破你的服務器防線他只需要在你的本地環境里“做點手腳”。更別提那些通過Web技術渲染的界面傳統的Web前端漏洞一樣可能存在。所以今天我想結合自己最近做的一個內部工具項目聊聊在Flexx環境下我們到底該怎么系統地構建安全防線。這個工具涉及一些內部數據的處理安全上絕對不能馬虎。我會從代碼層面、運行時層面、打包分發層面拆解每一個可能被忽視的漏洞點并給出可直接落地的加固方案。無論你是剛接觸Flexx還是已經用它做了些小工具這些實踐都能幫你把應用的安全水位提升一個檔次。2. 核心安全模型與威脅分析在動手加固之前我們得先搞清楚Flexx應用面臨的安全環境到底是什么樣的。你不能用防守城堡的思路去防守一艘船它們的威脅模型根本不同。2.1 Flexx應用的獨特架構與攻擊面一個典型的、使用flexx build命令打包后的Flexx桌面應用運行起來后其實包含了這么幾個部分一個本地HTTP服務器通常運行在localhost的某個端口上比如localhost:8080。這是應用的核心負責渲染UI和處理前端發來的事件。一個嵌入式的瀏覽器引擎通常是CEFChromium Embedded Framework用來加載和顯示那個本地服務器提供的頁面。你的Python業務邏輯代碼這些代碼被打包進了可執行文件隨著應用一起分發。這個架構決定了它的主要攻擊面本地服務暴露那個localhost:8080的服務雖然對外網不可見但在本機上是開放的。這意味著同一臺機器上的其他惡意程序可以嘗試連接這個端口發送精心構造的請求試圖觸發你后端邏輯里的漏洞比如命令注入、路徑遍歷。客戶端代碼“不可信”在Flexx里前端JS和后端Python通信非常方便但別忘了最終運行在瀏覽器里的JS代碼用戶是可以查看和調試的。雖然核心邏輯在Python端但前端代碼可能包含一些調用接口的“路徑”信息攻擊者可以分析這些接口嘗試直接調用或進行參數污染。打包文件的逆向風險你的Python代碼被打包進了一個可執行文件如.exe或.app。對于有一定技術的攻擊者他可以通過反編譯、內存dump等手段嘗試還原出你的部分甚至全部源代碼。如果代碼里寫了硬編碼的密鑰、API地址、內部邏輯那就全暴露了。傳統的Web漏洞Flexx應用的前端仍然是HTML/JS/CSS通過瀏覽器引擎渲染。所以如果前端代碼編寫不當導致產生了真正的DOM型XSS漏洞攻擊者雖然不能直接竊取服務器數據因為沒服務器但可以操縱你的應用界面進行釣魚或者進行本地文件操作如果應用有相關權限。理解這些攻擊面是我們制定所有安全措施的基礎。你的加固工作必須圍繞著“保護本地服務”、“混淆核心邏輯”、“凈化輸入輸出”這幾個核心點展開。2.2 從熱詞看實際威脅場景最近ctfshow這類平臺出現了很多關于“Web應用安全與防護”的題目特別是Windows環境下的。這其實反映了一個趨勢大家越來越關注客戶端應用的安全了。這些題目里經常考察的點比如通過構造特殊輸入進行本地文件讀取、利用應用邏輯缺陷提升權限、分析客戶端代碼找到隱藏接口等完全可能發生在你的Flexx應用上。另一個熱詞“web應用打包桌面應用”點明了Flexx這類技術的用途。大家喜歡它就是因為“一次編寫多處運行”還能有原生應用的體驗。但安全意識的滯后往往就在這里埋雷。開發者可能覺得“反正就跑在用戶自己電腦上能出啥大事” 這種想法很危險。如果這個工具處理的是個人敏感信息如密碼管理器、公司內部數據或者具有某些系統操作權限一旦被攻破后果可能很嚴重。所以我們的安全實踐必須假設運行環境是“惡意”的或者至少是“不可完全信任”的。用戶可能無意中運行了惡意軟件也可能主動嘗試破解你的應用。我們的目標不是制造一個無法破解的“黑盒”那幾乎不可能而是顯著提高攻擊的成本和難度讓絕大多數潛在攻擊者望而卻步。3. 代碼層面的防御構建安全的第一道墻一切安全的基礎都源于你寫下的每一行代碼。對于Flexx應用我們需要在前后端都建立起堅固的防線。3.1 后端Python輸入驗證與凈化這是防御本地服務被攻擊的核心。所有從前端通過Flexx的事件機制傳來的數據都必須視為不可信的。原則白名單驗證優于黑名單過濾。不要試圖去猜測所有惡意輸入長什么樣而是明確定義什么是合法的輸入。假設我們有一個功能讓用戶輸入一個文件名然后應用去讀取一個特定目錄下的這個文件。這是一個非常危險的操作如果處理不當就會造成路徑遍歷漏洞。錯誤示范from flexx import flx import os BASE_DIR “./data” class VulnerableApp(flx.Widget): def init(self): super().init() # ... 前端組件定義 flx.reaction(‘input_field.text’) def on_file_request(self, *events): for ev in events: filename ev.new_value # 直接信任前端輸入 filepath os.path.join(BASE_DIR, filename) try: with open(filepath, ‘r’) as f: content f.read() # 將內容發送回前端顯示 self.display_widget.set_text(content) except Exception as e: self.display_widget.set_text(f“Error: {e}”)這段代碼的問題太大了。攻擊者可以在前端輸入../../../etc/passwdos.path.join可能會生成一個指向系統敏感文件的路徑導致信息泄露。加固后的實踐from flexx import flx import os import posixpath # 使用posixpath處理路徑更安全 BASE_DIR os.path.abspath(“./data”) # 使用絕對路徑 ALLOWED_EXTENSIONS {‘.txt’, ‘.json’, ‘.csv’} # 定義允許的文件后綴 class SecureApp(flx.Widget): def init(self): super().init() # ... 前端組件定義 flx.reaction(‘input_field.text’) def on_file_request(self, *events): for ev in events: user_input ev.new_value.strip() # 1. 驗證輸入不為空且是基本字符串 if not user_input or not isinstance(user_input, str): self._log_security_event(“invalid_input_type”, user_input) return # 2. 白名單過濾文件名只允許字母、數字、下劃線、點和短橫線且不能以點開頭防隱藏文件 import re if not re.match(r‘^[a-zA-Z0-9_\-][a-zA-Z0-9_\-\.]*$’, user_input): self._log_security_event(“invalid_filename_pattern”, user_input) return # 3. 檢查文件后綴 _, ext os.path.splitext(user_input) if ext.lower() not in ALLOWED_EXTENSIONS: self._log_security_event(“disallowed_extension”, user_input) return # 4. 規范化路徑防止目錄遍歷 # 先拼接然后確保最終路徑在BASE_DIR之內 requested_path os.path.join(BASE_DIR, user_input) normalized_path os.path.normpath(requested_path) # 關鍵檢查解析后的路徑是否仍然以BASE_DIR開頭 if not normalized_path.startswith(BASE_DIR): self._log_security_event(“path_traversal_attempt”, user_input) return # 5. 安全檢查確保最終路徑是一個文件并且存在 if not os.path.isfile(normalized_path): self._log_security_event(“not_a_file_or_not_exist”, normalized_path) return # 6. 一切檢查通過執行操作 try: with open(normalized_path, ‘r’, encoding‘utf-8’) as f: content f.read() self.display_widget.set_text(content) except Exception as e: # 注意錯誤信息不要透露內部路徑細節 self.display_widget.set_text(“無法讀取指定文件。”) self._log_error(e) def _log_security_event(self, event_type, detail): “”“記錄安全事件在實際應用中可寫入日志文件或發送到監控端”“” print(f“[SECURITY] {event_type}: {detail}”) # 示例應使用更安全的日志庫注意路徑檢查normalized_path.startswith(BASE_DIR)在Windows上可能因為路徑大小寫或分隔符問題需要額外處理。一個更健壯的方法是使用os.path.commonpath([BASE_DIR, normalized_path]) BASE_DIR。實操心得對于任何來自前端的數據無論是事件參數、回調函數參數還是通過flx.set_state設置的狀態都要執行嚴格的驗證。特別是當這些數據用于文件操作、系統命令調用強烈不建議在桌面應用中直接調用、數據庫查詢如果應用內嵌了SQLite時驗證必須格外嚴格。3.2 前端JS/React的XSS防御雖然Flexx幫你處理了大部分UI邏輯但你仍然可能通過flx.js或innerHTML等方式動態操作DOM這就引入了XSS風險。絕對避免使用innerHTML或outerHTML來插入未經驗證的用戶數據。如果非要動態生成HTML必須對數據進行HTML實體編碼。Flexx提供了flx.js來進行前端操作相對安全因為它通常操作的是組件屬性而非原始HTML。但如果你需要在Label等組件中顯示富文本要小心# 潛在風險 flx.Label(htmlf“bHello, {user_provided_name}/b”) # 如果user_provided_name包含script就完了 # 安全做法使用text屬性或者對輸入進行編碼 import html safe_name html.escape(user_provided_name) flx.Label(textf“Hello, {safe_name}”) # text屬性會自動處理更安全 # 或者如果必須用html確保編碼 flx.Label(htmlf“bHello, {safe_name}/b”)更佳實踐盡量使用Flexx組件的原生屬性如text、value來設置內容而不是html屬性。讓框架去處理渲染的細節。3.3 敏感信息處理與代碼混淆這是保護你知識產權和防止敏感信息泄露的關鍵。永遠不要在你的源代碼中硬編碼以下信息API密鑰、令牌、密碼數據庫連接字符串即使是本地SQLite加密密鑰用于本地加密存儲后端服務器地址如果你的應用需要聯網那么這些信息該放哪配置文件在應用首次運行時在用戶目錄如~/.yourapp/或%APPDATA%\Yourapp生成一個配置文件。將可配置的敏感信息放在這里。代碼中只包含一個“初始空值”或“從環境變量讀取”的邏輯。import os import json from pathlib import Path CONFIG_DIR Path.home() / “.my_flexx_app” CONFIG_FILE CONFIG_DIR / “config.json” def load_config(): if not CONFIG_FILE.exists(): # 首次運行創建包含默認值或空值的配置 default_config {“api_key”: “”, “user_token”: “”} CONFIG_DIR.mkdir(parentsTrue, exist_okTrue) with open(CONFIG_FILE, ‘w’) as f: json.dump(default_config, f) return default_config else: with open(CONFIG_FILE, ‘r’) as f: return json.load(f) # 在應用初始化時加載 app_config load_config() API_KEY app_config.get(‘api_key’) # 從配置文件讀取環境變量對于開發階段或某些部署場景可以通過環境變量傳遞。import os API_KEY os.environ.get(‘MYAPP_API_KEY’, ‘’) # 優先從環境變量讀沒有則用空字符串代碼混淆與打包優化使用PyInstaller或Nuitka打包時可以利用其混淆選項。PyInstaller使用--key參數對字節碼進行加密但請注意這并非絕對安全只是增加逆向難度。pyinstaller —onefile —windowed —keyYourRandomKey16Bytes your_script.pyNuitka直接將Python編譯成C代碼再編譯成二進制逆向難度比PyInstaller的字節碼打包要高得多。剝離調試信息確保打包時去除了所有pdb斷點、詳細日志和調試符號。重要提醒代碼混淆和加密只能提高門檻無法完全阻止逆向工程。安全的核心不應依賴于代碼的保密性而應依賴于設計即使攻擊者拿到了全部源代碼他也無法輕易地獲取敏感數據或進行未授權操作。這意味著真正的密鑰、核心驗證邏輯最好放在一個遠程服務器上如果應用需要聯網或者依賴于用戶本地系統的安全機制如Keychain、Credential Manager。4. 構建與分發加固鎖好應用的“發布門”代碼寫安全了下一步是確保打包和分發過程不會引入新的漏洞或泄露信息。4.1 安全的打包配置與依賴管理你的setup.py或pyproject.toml以及打包命令都需要仔細檢查。清理__pycache__和臨時文件在打包前確保你的項目目錄里沒有.pyc緩存文件、臨時日志文件、包含敏感信息的測試配置文件。可以在打包腳本里加入清理步驟。# 一個簡單的打包前清理腳本 clean.sh 或 clean.bat find . -type d -name “__pycache__” -exec rm -rf {} find . -type f -name “*.pyc” -delete find . -type f -name “*.log” -delete rm -rf ./dist ./build # 清理舊的打包目錄最小化依賴在requirements.txt或setup.py中只聲明應用運行所必需的最小依賴集。每個多余的依賴都可能帶來未知的安全漏洞。定期用pip-audit或safety檢查依賴是否有已知漏洞。pip install safety safety check -r requirements.txt使用虛擬環境打包永遠不要在系統Python環境下直接打包。使用venv或conda創建一個干凈的虛擬環境在里面安裝依賴然后從這個環境打包。這能避免混入你開發機器上的無關可能帶有敏感信息的包。4.2 發布物檢查與簽名打包生成的可執行文件.exe,.app,.dmg等就是你要分發給用戶的最終產品。防病毒軟件誤報用PyInstaller打包的Python程序尤其是加了—onefile選項的非常容易被Windows Defender等殺毒軟件誤報為病毒。這不是你的代碼有問題而是打包方式單文件自解壓和行為啟動子進程觸發了啟發式掃描。緩解措施1考慮使用—onedir目錄模式而非—onefile單文件模式。誤報率會低一些但分發起來是多個文件。緩解措施2為你的應用申請代碼簽名證書Code Signing Certificate。雖然需要花錢個人開發者可以考慮便宜的證書或開源項目的免費選項但這是解決誤報和建立用戶信任最有效的方式。簽名后的應用Windows SmartScreen等安全機制會更信任它。緩解措施3在應用發布頁面明確說明如果遇到殺毒軟件報警可能是誤報并指導用戶如何將你的應用加入白名單。完整性校驗提供安裝包或可執行文件的哈希值如SHA256讓用戶可以校驗下載的文件是否被篡改。# 在發布時生成 shasum -a 256 MyFlexxApp.dmg將得到的哈希值公布在下載頁面。分發渠道安全盡可能通過官方應用商店如Mac App Store, Microsoft Store或你自己的HTTPS網站分發。避免通過網盤鏈接、論壇附件等不可控的方式傳播防止中間人被篡改。5. 運行時防護與監控應用到了用戶手里安全戰斗才剛剛開始。我們需要讓應用在運行時也能抵御攻擊。5.1 本地服務隔離與訪問控制默認情況下Flexx的開發服務器綁定在0.0.0.0所有接口或localhost。對于打包后的應用必須確保服務只綁定在127.0.0.1環回地址這樣只有本機進程可以訪問同一局域網的其他機器無法連接。在啟動你的Flexx應用時明確指定hostif __name__ ‘__main__’: # 開發時可能用 ‘0.0.0.0’ 方便調試但發布版一定要用 ‘127.0.0.1’ import os is_dev os.environ.get(‘FLEXX_DEV’, ‘0’) ‘1’ host ‘0.0.0.0’ if is_dev else ‘127.0.0.1’ port 8080 app flx.App(MySecureApp) app.launch(‘app’, hosthost, portport) # 使用 launch 方法并指定 host flx.run()更進一步可以為本地服務設置一個簡單的令牌驗證雖然同一機器上的其他程序理論上都能訪問但增加一層簡單的挑戰響應可以攔截很多簡單的自動化掃描腳本。不過要注意這個令牌不能硬編碼在JS里否則形同虛設。可以考慮在應用啟動時動態生成并通過進程間通信IPC傳遞給渲染進程但這在Flexx中實現較為復雜需權衡安全收益和復雜度。一個更實用的方法是隨機化端口。每次啟動應用時隨機選擇一個可用端口而不是固定使用8080。這增加了攻擊者探測的難度。import socket def find_free_port(): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind((‘127.0.0.1’, 0)) # 綁定到0端口系統會分配一個空閑端口 return s.getsockname()[1] port find_free_port()5.2 日志與異常處理的安全要點日志是事后追溯和分析攻擊的關鍵但錯誤的日志記錄方式本身就會導致信息泄露。禁止記錄敏感信息絕對不要在日志、打印語句或錯誤信息中記錄密碼、密鑰、令牌、完整的個人身份信息PII。結構化日志使用structlog或logging模塊的Formatter來記錄結構化日志。方便后續集中分析和告警。區分日志級別將安全相關事件如登錄失敗、路徑遍歷嘗試、輸入驗證失敗記錄在WARNING或ERROR級別并帶上明確的標識如[SECURITY]前綴。安全的異常反饋給前端用戶的錯誤信息應該是模糊的、友好的但后臺日志必須是詳細的。try: # … 一些危險操作 result dangerous_operation(user_input) except PermissionError: # 給用戶看 self.show_error(“您沒有執行此操作的權限。”) # 后臺記錄 logger.error(f“[SECURITY] Permission denied for user_input{user_input} from ip{request_ip}”) except Exception as e: # 給用戶看通用錯誤 self.show_error(“操作失敗請重試或聯系管理員。”) # 后臺記錄詳細異常包括堆棧 logger.exception(f“[ERROR] Operation failed with input: {user_input}”)5.3 資源訪問與權限最小化你的應用應該只請求它正常運行所必需的權限。文件系統訪問使用明確的、受控的目錄。如前所述用BASE_DIR限定文件操作范圍。如果需要用戶選擇文件使用系統文件對話框Flexx可能需借助pywebview或其他原生橋接而不是讓用戶自由輸入路徑。網絡訪問如果你的應用需要聯網要明確是只連特定的可信域名還是可以訪問任意地址。可以考慮使用requests庫并為其設置超時和重試策略避免被惡意服務器拖住。import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry session requests.Session() retry_strategy Retry( total3, backoff_factor1, status_forcelist[429, 500, 502, 503, 504], ) adapter HTTPAdapter(max_retriesretry_strategy) session.mount(“http://”, adapter) session.mount(“https://”, adapter) # 發起請求時使用timeout try: response session.get(‘https://api.trusted.com/data‘, timeout(3.05, 27)) response.raise_for_status() except requests.exceptions.Timeout: logger.error(“API request timed out”) except requests.exceptions.RequestException as e: logger.error(f“Network request failed: {e}”)子進程執行極其不推薦在桌面應用中執行系統命令os.system,subprocess.run。如果萬不得已例如調用一個外部工具必須使用subprocess.run()的shellFalse模式默認。對命令參數進行嚴格的白名單驗證。設置超時。限制子進程的資源如CPU、內存。6. 常見安全問題排查與應急響應即使做了萬全準備也可能遇到問題。這里記錄幾個我實際遇到過的場景和排查思路。6.1 應用啟動失敗或崩潰癥狀用戶雙擊應用沒反應或閃退。排查查看日志首先檢查應用是否生成了日志文件。你可以在代碼中設置將日志寫入到用戶目錄的固定位置。命令行啟動指導用戶嘗試通過命令行啟動應用對于.exe在cmd中運行對于.app通過終端運行。這能直接看到Python或運行時的錯誤輸出往往是缺失依賴、路徑錯誤或權限問題。依賴沖突確保打包環境是干凈的并且所有依賴版本都被正確鎖定。一個常見的坑是PyInstaller可能沒有打包某些隱式依賴的.dll或.so文件。使用—hidden-import手動指定。殺毒軟件攔截這是最常見的原因之一。讓用戶暫時禁用殺毒軟件試試如果成功那就印證了誤報問題需要推動代碼簽名或向殺毒軟件廠商提交誤報申訴。6.2 功能異常或數據泄露癥狀某個功能突然不正常或者應用似乎輸出了不該輸出的信息。排查檢查輸入第一時間懷疑所有用戶輸入點。是否有一個輸入框沒有做驗證是否有一個API接口暴露了過多的錯誤詳情審查日志查看安全事件日志是否有大量的驗證失敗記錄這可能是有腳本在自動化探測你的接口。本地網絡掃描用netstat -anWindows/Linux或lsof -iMac檢查你的應用是否在監聽預期的端口如127.0.0.1:某個端口并且沒有意外綁定到0.0.0.0。模擬攻擊自己扮演攻擊者使用Burp Suite配置代理到127.0.0.1或簡單的Python腳本嘗試向你的應用本地端口發送各種畸形、超長、包含特殊字符的請求觀察應用的反應。6.3 安全事件響應清單如果懷疑應用被惡意利用應該有一個簡單的響應流程隔離如果可能通知用戶立即停止使用該版本應用。取證收集用戶的日志文件如果之前設計了日志功能。查看是否有異常模式的安全事件記錄。分析根據日志和用戶描述嘗試復現問題。確定漏洞的根本原因是輸入驗證缺失是路徑遍歷還是信息泄露修復在開發環境中修復漏洞。修復原則是“最小修補”即用最小的改動堵上漏洞并添加相應的測試用例。更新發布安全更新版本。更新日志中應簡要、模糊地說明修復了一個安全問題而不要透露漏洞細節以免被更多人利用。通知如果漏洞影響較大如可能導致用戶數據泄露應考慮通過郵件、應用內通知等方式告知受影響的用戶建議他們升級。7. 進階考量當你的Flexx應用需要聯網很多Flexx應用不僅是本地工具還需要與后端API交互。這引入了全新的安全維度。7.1 通信安全 (HTTPS與證書鎖定)強制HTTPS所有與后端服務器的通信必須使用HTTPSTLS/SSL。不要使用HTTP即使在內部網絡也不要。證書驗證requests庫默認會驗證服務器證書。永遠不要在代碼中設置verifyFalse來跳過證書驗證這會使中間人攻擊變得輕而易舉。證書鎖定Certificate Pinning對于安全性要求極高的應用可以考慮證書鎖定。這意味著你的客戶端只信任你預期的服務器持有的特定證書或公鑰哈希而不是信任操作系統或瀏覽器的整個根證書庫。這能有效防御攻擊者使用自己簽發的證書進行的中間人攻擊。實現思路在代碼中嵌入服務器證書的公鑰指紋SHA256。在發起HTTPS請求時使用requests的適配器在urllib3層面添加對證書指紋的驗證。注意事項證書鎖定會使證書輪換變得困難。你需要規劃好如何安全地更新客戶端內嵌的指紋。7.2 身份認證與授權避免在客戶端存儲長期有效的令牌如果用戶需要登錄服務器應該頒發一個短期的訪問令牌如JWT有效期幾小時和一個長期的刷新令牌。客戶端將刷新令牌安全存儲如使用系統鑰匙串用其獲取新的訪問令牌。訪問令牌只存在內存中應用關閉即失效。安全的令牌存儲Windows使用win32cryptpywin32的一部分將令牌存儲在Windows Credential Manager。macOS使用keyring庫后端是Keychain。Linux使用keyring庫后端可能是Secret Service。這樣存儲的令牌其他普通應用程序無法直接讀取安全性比放在明文文件或注冊表里高得多。權限細分如果應用有不同的功能模塊后端API設計時應遵循最小權限原則。前端持有的令牌只應擁有完成當前用戶操作所必需的權限而不是萬能鑰匙。7.3 數據加密存儲如果應用需要在本地存儲敏感數據如用戶緩存的加密筆記、配置信息使用強加密算法如AES-256-GCM。GCM模式同時提供加密和完整性驗證。密鑰管理是關鍵加密密鑰不能硬編碼。可以采用“用戶密碼派生密鑰”的方式如果應用有登錄密碼或者使用系統提供的安全存儲如上述的鑰匙串來保存一個主密鑰。完整示例簡化版from cryptography.fernet import Fernet # 這是一個使用AES-128-CBC和HMAC的易用庫 from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.primitives import hashes import base64 import os def derive_key_from_password(password: str, salt: bytes) - bytes: “”“使用PBKDF2從密碼派生密鑰”“” kdf PBKDF2HMAC( algorithmhashes.SHA256(), length32, saltsalt, iterations480000, # 迭代次數要高增加暴力破解成本 ) return base64.urlsafe_b64encode(kdf.derive(password.encode())) # 假設我們從安全的地方獲取了密碼和鹽 user_password “user_provided_password” salt os.urandom(16) # 鹽需要和加密數據一起安全地存儲 key derive_key_from_password(user_password, salt) cipher Fernet(key) # 加密數據 sensitive_data “This is a secret message”.encode() encrypted_data cipher.encrypt(sensitive_data) # 現在可以將 encrypted_data 和 salt 存儲在一起 # 解密時用同樣的密碼和存儲的鹽派生密鑰然后解密 # stored_salt … 從存儲中讀取 # stored_encrypted_data … 從存儲中讀取 # key2 derive_key_from_password(user_password, stored_salt) # cipher2 Fernet(key2) # decrypted_data cipher2.decrypt(stored_encrypted_data)警告本地加密只能防止應用數據文件被直接偷走后讀取。如果攻擊者能在你的應用運行時進行內存掃描他可能提取到解密后的密鑰或數據。這需要更高級的對抗措施已超出一般桌面應用的范疇。安全是一個持續的過程而不是一次性的任務。對于Flexx桌面應用你需要時刻記住它的混合特性既有Web應用的常見漏洞又有桌面應用的本地安全挑戰。從代碼編寫的第一行起就繃緊安全這根弦在構建、分發、運行時層層設防才能打造出讓用戶放心使用的可靠產品。我最深的體會是很多安全問題都源于“想當然”和“圖省事”。多花半小時驗證輸入多寫幾行代碼做路徑檢查在項目初期就規劃好配置管理和日志這些投入在長遠來看會為你省下無數排查漏洞和應對安全事件的時間。