# HBR Taiwan 爬蟲系統 - 安全審計報告 **審計日期**: 2024-12-22 **審計員**: Claude (AI Security Consultant) **報告版本**: 1.0 **風險評級**: 🔴 **高風險** - 發現災難級漏洞,需立即修復 --- ## 基本專案資訊 | 項目 | 內容 | |------|------| | **專案名稱** | HBR Taiwan Web Scraper | | **專案描述** | 自動化爬取 Harvard Business Review Taiwan 文章的系統 | | **目標用戶** | 內部/個人使用 | | **處理的資料類型** | 文章元資料(標題、URL、作者、日期、分類、標籤)、文章內容 | | **是否處理 PII** | 否 | | **是否處理付款資訊** | 否 | | **是否有 UGC** | 否 | | **技術棧** | Python 3.11+, Scrapy, MySQL/MariaDB, Gmail SMTP | | **部署環境** | GitHub Actions (私有倉庫), 本地 Crontab | | **外部依賴** | scrapy>=2.11.0, itemadapter>=0.7.0, pymysql>=1.1.0, python-dateutil>=2.8.2 | | **資料庫位置** | mysql.theaken.com:33306 (公網可訪問) | | **未來計畫** | 將新增 Web 介面/API | --- ## 執行摘要 本次審計發現 **3 個高風險**、**4 個中風險**、**3 個低風險** 問題。最嚴重的問題是 **資料庫密碼硬編碼在多個原始碼檔案中**,且該資料庫可從公網訪問。這是一個典型的「新手災難級錯誤」,必須立即修復。 ### 風險統計 | 風險等級 | 數量 | 狀態 | |----------|------|------| | 🔴 高風險 (High) | 3 | 需立即修復 | | 🟠 中風險 (Medium) | 4 | 建議盡快修復 | | 🟢 低風險 (Low) | 3 | 建議改善 | --- ## 第一部分:災難級新手錯誤檢查 ### 🔴 HIGH-001: 資料庫密碼硬編碼在原始碼中 * **風險等級**: `High` - 災難級 * **威脅描述**: 資料庫連線密碼 `Aa123456` 直接硬編碼在多個 Python 檔案中,包括 `settings.py`、`database.py` 和 `test_db_connection.py`。由於資料庫伺服器可從公網訪問,任何能夠看到這些檔案的人都可以直接連線到您的資料庫。 * **受影響組件**: - `hbr_crawler/hbr_crawler/settings.py` (第 64-68 行) - `hbr_crawler/hbr_crawler/database.py` (第 220, 227 行) - `test_db_connection.py` (第 26-32 行) --- **駭客劇本 (Hacker's Playbook)**: > 「我是一個好奇的人,偶然間在某個地方看到了這個專案的原始碼(可能是開發者不小心把私有倉庫設成公開、在論壇分享了程式碼片段、或者我拿到了開發者的電腦存取權限)。 > > 我打開 `settings.py`,哇!第 67 行寫著 `DB_PASSWORD = 'Aa123456'`。再往上看,主機是 `mysql.theaken.com`,埠號是 `33306`,用戶名是 `A101`,資料庫名是 `HBR_scraper`。 > > 我打開終端機,輸入: > ```bash > mysql -h mysql.theaken.com -P 33306 -u A101 -pAa123456 > ``` > > 成功連線!現在我可以: > 1. **讀取所有資料** - `SELECT * FROM articles;` > 2. **刪除所有資料** - `DROP TABLE articles;`(如果用戶權限允許) > 3. **植入惡意資料** - 在文章內容中注入惡意腳本 > 4. **竊取其他資料庫** - 如果這個用戶有權限訪問其他資料庫 > 5. **作為跳板** - 使用這個資料庫伺服器攻擊同一網路中的其他系統 > > 更糟糕的是,密碼 `Aa123456` 是一個極弱的密碼,即使我沒看到原始碼,使用暴力破解工具幾分鐘內就能猜出來。」 --- **修復原理 (Principle of the Fix)**: > 為什麼不能把密碼寫在程式碼裡?想像一下,你的程式碼是一本「食譜書」,你會把食譜印成書賣給很多人看。你會在食譜裡寫「我家保險箱的密碼是 123456」嗎?當然不會! > > 正確的做法是把密碼放在「環境變數」裡。環境變數就像是寫在便條紙上、只有你自己能看到的秘密。程式執行時會去讀這張便條紙,但便條紙不會被複製到食譜書裡。 > > 在不同環境(開發、測試、生產),你可以用不同的便條紙(不同的環境變數),這樣也更安全、更靈活。 --- * **修復建議與程式碼範例**: **步驟 1**: 建立 `.env` 檔案(此檔案絕對不能提交到版本控制) ```bash # .env (放在專案根目錄) DB_HOST=mysql.theaken.com DB_PORT=33306 DB_USER=A101 DB_PASSWORD=您的新強密碼 DB_NAME=HBR_scraper ``` **步驟 2**: 建立 `.gitignore` 檔案(如果未來要使用 Git) ```gitignore # .gitignore .env *.env .env.* __pycache__/ *.pyc .venv/ venv/ hbr_articles.csv *.log ``` **步驟 3**: 安裝 python-dotenv ```bash pip install python-dotenv ``` 並更新 `requirements.txt`: ``` scrapy>=2.11.0 itemadapter>=0.7.0 pymysql>=1.1.0 python-dateutil>=2.8.2 python-dotenv>=1.0.0 ``` **步驟 4**: 修改 `settings.py` ```python # 修改前 (危險!) DB_HOST = 'mysql.theaken.com' DB_PORT = 33306 DB_USER = 'A101' DB_PASSWORD = 'Aa123456' # 🔴 密碼暴露! DB_NAME = 'HBR_scraper' # 修改後 (安全) import os from dotenv import load_dotenv # 載入 .env 檔案 load_dotenv() DB_HOST = os.environ.get('DB_HOST', 'localhost') DB_PORT = int(os.environ.get('DB_PORT', 3306)) DB_USER = os.environ.get('DB_USER') DB_PASSWORD = os.environ.get('DB_PASSWORD') DB_NAME = os.environ.get('DB_NAME') # 檢查必要的環境變數 if not all([DB_USER, DB_PASSWORD, DB_NAME]): raise ValueError("Missing required database environment variables: DB_USER, DB_PASSWORD, DB_NAME") ``` **步驟 5**: 修改 `database.py` 的 `get_database_manager()` 函數 ```python # 修改前 (危險!) def get_database_manager() -> DatabaseManager: import os try: from scrapy.utils.project import get_project_settings settings = get_project_settings() host = settings.get('DB_HOST', os.environ.get('DB_HOST', 'mysql.theaken.com')) # ... 預設值包含真實密碼 ... password = settings.get('DB_PASSWORD', os.environ.get('DB_PASSWORD', 'Aa123456')) # 🔴 except: password = os.environ.get('DB_PASSWORD', 'Aa123456') # 🔴 # 修改後 (安全) def get_database_manager() -> DatabaseManager: import os from dotenv import load_dotenv load_dotenv() try: from scrapy.utils.project import get_project_settings settings = get_project_settings() host = settings.get('DB_HOST') or os.environ.get('DB_HOST') port = settings.getint('DB_PORT', int(os.environ.get('DB_PORT', 3306))) user = settings.get('DB_USER') or os.environ.get('DB_USER') password = settings.get('DB_PASSWORD') or os.environ.get('DB_PASSWORD') database = settings.get('DB_NAME') or os.environ.get('DB_NAME') except: host = os.environ.get('DB_HOST') port = int(os.environ.get('DB_PORT', 3306)) user = os.environ.get('DB_USER') password = os.environ.get('DB_PASSWORD') database = os.environ.get('DB_NAME') if not all([host, user, password]): raise ValueError("Database configuration missing. Please set environment variables.") return DatabaseManager(host, port, user, password, database) ``` **步驟 6**: 修改 `test_db_connection.py` ```python # 修改前 (危險!) DB_CONFIG = { 'host': 'mysql.theaken.com', 'port': 33306, 'user': 'A101', 'password': 'Aa123456', # 🔴 'database': 'HBR_scraper' } # 修改後 (安全) import os from dotenv import load_dotenv load_dotenv() DB_CONFIG = { 'host': os.environ.get('DB_HOST'), 'port': int(os.environ.get('DB_PORT', 3306)), 'user': os.environ.get('DB_USER'), 'password': os.environ.get('DB_PASSWORD'), 'database': os.environ.get('DB_NAME') } # 驗證配置 if not all([DB_CONFIG['host'], DB_CONFIG['user'], DB_CONFIG['password']]): print("錯誤: 請設定環境變數 DB_HOST, DB_USER, DB_PASSWORD") sys.exit(1) ``` **步驟 7**: 立即更換資料庫密碼 由於密碼已經暴露在原始碼中,即使您現在修復了程式碼,原來的密碼可能已經被他人知道。請: 1. 登入 MySQL 伺服器 2. 更換 A101 用戶的密碼為強密碼(至少 16 字元,包含大小寫字母、數字、特殊符號) 3. 更新 `.env` 檔案使用新密碼 4. 更新 GitHub Actions secrets ```sql -- 在 MySQL 中更換密碼 ALTER USER 'A101'@'%' IDENTIFIED BY '新的強密碼'; FLUSH PRIVILEGES; ``` --- ### 🔴 HIGH-002: 弱密碼 - 資料庫密碼強度不足 * **風險等級**: `High` * **威脅描述**: 目前使用的密碼 `Aa123456` 是一個極其常見的弱密碼,在大多數密碼字典攻擊中會在前 100 個嘗試內被破解。 * **受影響組件**: MySQL 用戶 `A101` --- **駭客劇本 (Hacker's Playbook)**: > 「即使我沒有看到原始碼,我知道 `mysql.theaken.com:33306` 是一個公開的 MySQL 伺服器。我啟動 Hydra(一個密碼暴力破解工具),使用常見密碼字典: > > ```bash > hydra -l A101 -P /usr/share/wordlists/rockyou.txt mysql://mysql.theaken.com:33306 > ``` > > 不到一分鐘,工具告訴我密碼是 `Aa123456`。這個密碼太常見了,幾乎每個密碼字典都有它。」 --- * **修復建議**: 1. 使用密碼管理器生成強密碼(建議 20+ 字元) 2. 密碼應包含: - 大寫字母 (A-Z) - 小寫字母 (a-z) - 數字 (0-9) - 特殊符號 (!@#$%^&*) 3. 避免使用: - 字典單詞 - 常見模式(123456, password, qwerty) - 個人資訊(生日、名字) **強密碼範例** (請勿直接使用,僅作示例): ``` Hbr$Cr@wl3r#2024!Secure_DB ``` --- ### 🔴 HIGH-003: 資料庫伺服器公網暴露且缺乏防護 * **風險等級**: `High` * **威脅描述**: MySQL 伺服器 `mysql.theaken.com:33306` 可從公網直接訪問,沒有 IP 白名單限制,增加了被暴力破解和未授權訪問的風險。 * **受影響組件**: MySQL 伺服器配置 --- **駭客劇本 (Hacker's Playbook)**: > 「我使用 Shodan(物聯網搜尋引擎)搜尋開放的 MySQL 伺服器: > > ``` > port:33306 product:MySQL > ``` > > 發現 `mysql.theaken.com` 出現在列表中。這個伺服器對全世界開放,任何人都可以嘗試連線。我可以: > 1. 使用暴力破解工具嘗試常見用戶名/密碼組合 > 2. 嘗試已知的 MySQL 漏洞 > 3. 進行 DoS 攻擊使服務不可用」 --- * **修復建議**: **方案 A**: 設定防火牆白名單(推薦) ```bash # 在伺服器上設定 iptables,只允許特定 IP 訪問 # 假設您的 IP 是 203.0.113.100,GitHub Actions IP 範圍需另外查詢 # 先封鎖所有對 33306 埠的訪問 iptables -A INPUT -p tcp --dport 33306 -j DROP # 允許特定 IP iptables -I INPUT -p tcp -s 203.0.113.100 --dport 33306 -j ACCEPT # 允許 GitHub Actions IP 範圍(需定期更新) # 可從 https://api.github.com/meta 獲取 ``` **方案 B**: 使用 SSH 隧道 ```bash # 不直接暴露 MySQL,而是通過 SSH 隧道連線 ssh -L 3306:localhost:3306 user@mysql.theaken.com # 然後本地連線 mysql -h 127.0.0.1 -P 3306 -u A101 -p ``` **方案 C**: 使用 VPN 設定 VPN 伺服器,只有連接到 VPN 後才能訪問資料庫。 **方案 D**: MySQL 用戶 IP 限制 ```sql -- 限制 A101 用戶只能從特定 IP 連線 -- 先刪除現有用戶 DROP USER 'A101'@'%'; -- 創建只允許從特定 IP 連線的用戶 CREATE USER 'A101'@'203.0.113.100' IDENTIFIED BY '新的強密碼'; GRANT ALL PRIVILEGES ON HBR_scraper.* TO 'A101'@'203.0.113.100'; -- 如果需要 GitHub Actions,需要為其 IP 範圍創建用戶 -- 注意:GitHub Actions IP 範圍很廣,此方案可能不實際 ``` --- ## 第二部分:標準應用程式安全審計 ### 🟠 MEDIUM-001: 缺少 .gitignore 檔案 * **風險等級**: `Medium` * **威脅描述**: 專案沒有 `.gitignore` 檔案。如果未來將專案加入版本控制,可能會意外提交敏感檔案(如 `.env`、`hbr_articles.csv`、`__pycache__`)。 * **受影響組件**: 整個專案 * **修復建議**: 創建 `.gitignore` 檔案: ```gitignore # 環境變數(最重要!) .env .env.* *.env # Python __pycache__/ *.py[cod] *$py.class *.so .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ *.egg-info/ .installed.cfg *.egg # 虛擬環境 .venv/ venv/ ENV/ # IDE .idea/ .vscode/ *.swp *.swo # 專案特定 hbr_articles.csv *.csv *.log logs/ # Scrapy .scrapy/ # 資料庫備份 *.sql.bak *.sql.backup ``` --- ### 🟠 MEDIUM-002: SQL 注入風險 - create_database 函數 * **風險等級**: `Medium` * **威脅描述**: `database.py` 中的 `create_database` 函數使用字串格式化構建 SQL 語句,而非參數化查詢,存在 SQL 注入風險。 * **受影響組件**: `hbr_crawler/hbr_crawler/database.py` (第 107 行) ```python # 危險的程式碼 cursor.execute(f"CREATE DATABASE IF NOT EXISTS `{database_name}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci") ``` 雖然使用了反引號包裹,但如果 `database_name` 包含特殊字元(如反引號本身),仍可能導致 SQL 注入。 * **修復建議**: ```python # 修改後 - 驗證資料庫名稱 import re def create_database(self, database_name: str) -> bool: # 驗證資料庫名稱只包含安全字元 if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', database_name): logger.error(f"無效的資料庫名稱: {database_name}") return False try: with self.get_connection(None) as conn: with conn.cursor() as cursor: # 由於已驗證名稱,現在可以安全使用 cursor.execute(f"CREATE DATABASE IF NOT EXISTS `{database_name}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci") conn.commit() logger.info(f"資料庫 {database_name} 建立成功(或已存在)") return True except Exception as e: logger.error(f"建立資料庫失敗: {e}") return False ``` --- ### 🟠 MEDIUM-003: 資料庫連線未使用 SSL/TLS 加密 * **風險等級**: `Medium` * **威脅描述**: 雖然 SDD 文件提到應使用 SSL/TLS 加密連線,但實際程式碼中並未實作。資料在傳輸過程中以明文形式傳送,可能被中間人攻擊竊取。 * **受影響組件**: `hbr_crawler/hbr_crawler/database.py` (第 50-59 行) * **修復建議**: ```python # 修改前 connection = pymysql.connect( host=self.host, port=self.port, user=self.user, password=self.password, database=db_name, charset=self.charset, cursorclass=pymysql.cursors.DictCursor, autocommit=False ) # 修改後 - 加入 SSL 設定 import ssl def __init__(self, host: str, port: int, user: str, password: str, database: str = None, charset: str = 'utf8mb4', use_ssl: bool = True): # ... 原有程式碼 ... self.use_ssl = use_ssl @contextmanager def get_connection(self, database: Optional[str] = None): db_name = database or self.database # SSL 設定 ssl_config = None if self.use_ssl: ssl_config = { 'ssl': { 'ssl_disabled': False, 'check_hostname': True, 'verify_mode': ssl.CERT_REQUIRED, # 如果有 CA 證書,可以指定 # 'ca': '/path/to/ca-cert.pem' } } try: connect_args = { 'host': self.host, 'port': self.port, 'user': self.user, 'password': self.password, 'database': db_name, 'charset': self.charset, 'cursorclass': pymysql.cursors.DictCursor, 'autocommit': False } if ssl_config: connect_args.update(ssl_config) connection = pymysql.connect(**connect_args) yield connection connection.commit() # ... 原有錯誤處理 ... ``` --- ### 🟠 MEDIUM-004: 錯誤處理可能洩漏敏感資訊 * **風險等級**: `Medium` * **威脅描述**: 多處錯誤處理直接記錄完整的例外訊息,這些訊息可能包含敏感資訊(如資料庫連線字串、檔案路徑等)。當未來新增 Web 介面時,這些錯誤訊息可能會暴露給最終用戶。 * **受影響組件**: - `database.py` (多處 logger.error) - `pipelines.py` (多處 logger.error) * **修復建議**: ```python # 修改前 - 可能洩漏敏感資訊 logger.error(f"資料庫連線錯誤: {e}") # 修改後 - 安全的錯誤記錄 import traceback # 開發環境可以記錄詳細錯誤 if os.environ.get('DEBUG', 'false').lower() == 'true': logger.error(f"資料庫連線錯誤: {e}") logger.debug(traceback.format_exc()) else: # 生產環境只記錄錯誤類型,不記錄詳細訊息 logger.error(f"資料庫連線錯誤: {type(e).__name__}") # 當未來有 Web 介面時,返回給用戶的錯誤訊息 def get_user_friendly_error(e: Exception) -> str: """返回對用戶友善的錯誤訊息,不洩漏內部細節""" error_map = { 'OperationalError': '資料庫暫時無法連線,請稍後再試', 'IntegrityError': '資料處理錯誤,請聯繫管理員', 'ProgrammingError': '系統錯誤,請聯繫管理員', } return error_map.get(type(e).__name__, '發生未知錯誤,請稍後再試') ``` --- ### 🟢 LOW-001: 未實作資料庫連線池 * **風險等級**: `Low` * **威脅描述**: 每次資料庫操作都會建立新連線,效能較差且可能耗盡資料庫連線數。 * **受影響組件**: `hbr_crawler/hbr_crawler/database.py` * **修復建議**: ```python # 使用連線池 from dbutils.pooled_db import PooledDB class DatabaseManager: _pool = None @classmethod def get_pool(cls, host, port, user, password, database, charset='utf8mb4'): if cls._pool is None: cls._pool = PooledDB( creator=pymysql, maxconnections=10, mincached=2, maxcached=5, blocking=True, host=host, port=port, user=user, password=password, database=database, charset=charset, cursorclass=pymysql.cursors.DictCursor ) return cls._pool @contextmanager def get_connection(self, database: Optional[str] = None): conn = self.get_pool(...).connection() try: yield conn conn.commit() except: conn.rollback() raise finally: conn.close() # 歸還到連線池,非真正關閉 ``` 需要安裝 DBUtils: ```bash pip install DBUtils ``` --- ### 🟢 LOW-002: GitHub Actions 工作流程安全性可加強 * **風險等級**: `Low` * **威脅描述**: GitHub Actions 工作流程沒有明確限制權限,使用預設權限可能過於寬鬆。 * **受影響組件**: `.github/workflows/weekly.yml` * **修復建議**: ```yaml name: weekly-crawl on: schedule: - cron: "0 0 * * 1" workflow_dispatch: {} # 明確限制權限 permissions: contents: read jobs: crawl-and-mail: runs-on: ubuntu-latest # 限制可訪問的 secrets environment: production steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.11" # 使用 pip cache 加速 - name: Cache pip packages uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} - name: Install dependencies run: pip install -r requirements.txt - name: Run crawler run: | cd hbr_crawler scrapy crawl hbr - name: Send mail with CSV env: GMAIL_USERNAME: ${{ secrets.GMAIL_USERNAME }} GMAIL_APP_PASSWORD: ${{ secrets.GMAIL_APP_PASSWORD }} MAIL_TO: ${{ secrets.MAIL_TO }} # 加入資料庫環境變數 DB_HOST: ${{ secrets.DB_HOST }} DB_PORT: ${{ secrets.DB_PORT }} DB_USER: ${{ secrets.DB_USER }} DB_PASSWORD: ${{ secrets.DB_PASSWORD }} DB_NAME: ${{ secrets.DB_NAME }} run: python send_mail.py hbr_articles.csv - name: Upload CSV as artifact uses: actions/upload-artifact@v4 with: name: hbr_articles_csv path: hbr_articles.csv retention-days: 7 # 限制保留天數 ``` --- ### 🟢 LOW-003: 缺少輸入驗證 * **風險等級**: `Low` * **威脅描述**: 爬蟲提取的資料沒有經過驗證就直接儲存,可能導致 XSS 攻擊(當未來有 Web 介面時)或資料完整性問題。 * **受影響組件**: `hbr_crawler/hbr_crawler/spiders/hbr.py`, `pipelines.py` * **修復建議**: ```python # 在 pipelines.py 中加入資料清理 import html import re def sanitize_html(text: str) -> str: """移除或轉義 HTML 標籤""" if not text: return '' # 移除 script 標籤 text = re.sub(r']*>.*?', '', text, flags=re.DOTALL | re.IGNORECASE) # 轉義 HTML 特殊字元 text = html.escape(text) return text.strip() def validate_url(url: str) -> bool: """驗證 URL 格式""" pattern = r'^https?://[a-zA-Z0-9\-._~:/?#\[\]@!$&\'()*+,;=%]+$' return bool(re.match(pattern, url)) class DatabasePipeline: def process_item(self, item, spider): adapter = ItemAdapter(item) # 驗證 URL url = adapter.get('url', '') if not validate_url(url): logger.warning(f"無效的 URL: {url}") return item # 清理文字欄位 for field in ['title', 'summary', 'content', 'author']: if adapter.get(field): adapter[field] = sanitize_html(adapter[field]) # ... 原有邏輯 ... ``` --- ## 第三部分:OWASP Top 10 (2021) 檢查清單 | 編號 | 漏洞類型 | 狀態 | 說明 | |------|---------|------|------| | A01 | Broken Access Control | ⚠️ 需注意 | 未來 Web 介面需實作適當的存取控制 | | A02 | Cryptographic Failures | 🔴 問題 | 密碼硬編碼、弱密碼、未使用 SSL | | A03 | Injection | ⚠️ 部分問題 | `create_database` 有潛在 SQL 注入風險,其他查詢已使用參數化 | | A04 | Insecure Design | ✅ 尚可 | 基本設計合理 | | A05 | Security Misconfiguration | 🔴 問題 | 資料庫公網暴露、缺少 .gitignore | | A06 | Vulnerable Components | ⚠️ 需檢查 | 需定期更新依賴套件 | | A07 | Auth Failures | ⚠️ 需注意 | 未來 Web 介面需實作適當的身分驗證 | | A08 | Software/Data Integrity | ✅ 良好 | 使用 GitHub Actions 官方 actions | | A09 | Logging Failures | ⚠️ 需改善 | 日誌可能洩漏敏感資訊 | | A10 | SSRF | ✅ 不適用 | 爬蟲目標是固定的,無 SSRF 風險 | --- ## 第四部分:依賴套件安全性分析 ### 目前依賴 (requirements.txt) | 套件 | 版本要求 | 已知漏洞 | 建議 | |------|---------|---------|------| | scrapy | >=2.11.0 | 無重大漏洞 | 保持更新 | | itemadapter | >=0.7.0 | 無已知漏洞 | 保持更新 | | pymysql | >=1.1.0 | 無重大漏洞 | 保持更新 | | python-dateutil | >=2.8.2 | 無已知漏洞 | 保持更新 | ### 建議新增的安全相關套件 ``` # requirements.txt 建議更新 scrapy>=2.11.0 itemadapter>=0.7.0 pymysql>=1.1.0 python-dateutil>=2.8.2 python-dotenv>=1.0.0 # 環境變數管理 DBUtils>=3.0.0 # 資料庫連線池(可選) ``` ### 建議:設定依賴漏洞掃描 在 GitHub 倉庫啟用 Dependabot: ```yaml # .github/dependabot.yml version: 2 updates: - package-ecosystem: "pip" directory: "/" schedule: interval: "weekly" open-pull-requests-limit: 5 ``` --- ## 第五部分:未來 Web 介面安全建議 由於您計畫新增 Web 介面,以下是預防性安全建議: ### 1. 身分驗證與授權 - 使用成熟的身分驗證框架(如 Flask-Login, FastAPI-Users) - 實作 CSRF 保護 - 使用 JWT 或 Session-based 認證 - 實作速率限制防止暴力破解 ### 2. API 安全 - 所有 API 端點使用 HTTPS - 實作 CORS 限制 - 驗證所有輸入參數 - 使用 API Key 或 OAuth 進行認證 ### 3. XSS 防護 - 對所有輸出進行 HTML 轉義 - 使用 Content-Security-Policy 標頭 - 驗證和清理所有用戶輸入 ### 4. 資料庫查詢 - 繼續使用參數化查詢 - 實作查詢結果分頁(防止大量資料洩漏) - 限制返回欄位(不返回敏感欄位) --- ## 修復優先順序 ### 🔴 立即修復 (今天就要做) 1. **HIGH-001**: 從所有原始碼中移除硬編碼的密碼,改用環境變數 2. **HIGH-002**: 更換資料庫密碼為強密碼 3. **HIGH-003**: 設定資料庫存取限制(IP 白名單或 VPN) ### 🟠 本週修復 4. **MEDIUM-001**: 建立 .gitignore 檔案 5. **MEDIUM-002**: 修復 `create_database` SQL 注入風險 6. **MEDIUM-003**: 啟用資料庫 SSL/TLS 連線 7. **MEDIUM-004**: 改善錯誤處理,避免資訊洩漏 ### 🟢 可排程修復 8. **LOW-001**: 實作資料庫連線池 9. **LOW-002**: 加強 GitHub Actions 安全設定 10. **LOW-003**: 加入輸入驗證和資料清理 --- ## 結語 本次審計發現了幾個需要立即修復的嚴重安全問題,最關鍵的是 **資料庫密碼硬編碼** 和 **弱密碼** 問題。由於資料庫伺服器可從公網訪問,這些問題的風險被大幅放大。 好消息是,您的程式碼在其他方面相對安全: - ✅ 使用參數化查詢(大部分情況) - ✅ 遵守 robots.txt - ✅ 使用 Gmail App Password 而非一般密碼 - ✅ 合理的程式碼結構 請按照優先順序儘快修復這些問題,特別是在將專案部署到生產環境之前。 --- **報告結束** 如有任何問題,請隨時詢問。