- Scrapy 爬蟲框架,爬取 HBR 繁體中文文章 - Flask Web 應用程式,提供文章查詢介面 - SQL Server 資料庫整合 - 自動化排程與郵件通知功能 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
25 KiB
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。我打開終端機,輸入:
mysql -h mysql.theaken.com -P 33306 -u A101 -pAa123456成功連線!現在我可以:
- 讀取所有資料 -
SELECT * FROM articles;- 刪除所有資料 -
DROP TABLE articles;(如果用戶權限允許)- 植入惡意資料 - 在文章內容中注入惡意腳本
- 竊取其他資料庫 - 如果這個用戶有權限訪問其他資料庫
- 作為跳板 - 使用這個資料庫伺服器攻擊同一網路中的其他系統
更糟糕的是,密碼
Aa123456是一個極弱的密碼,即使我沒看到原始碼,使用暴力破解工具幾分鐘內就能猜出來。」
修復原理 (Principle of the Fix):
為什麼不能把密碼寫在程式碼裡?想像一下,你的程式碼是一本「食譜書」,你會把食譜印成書賣給很多人看。你會在食譜裡寫「我家保險箱的密碼是 123456」嗎?當然不會!
正確的做法是把密碼放在「環境變數」裡。環境變數就像是寫在便條紙上、只有你自己能看到的秘密。程式執行時會去讀這張便條紙,但便條紙不會被複製到食譜書裡。
在不同環境(開發、測試、生產),你可以用不同的便條紙(不同的環境變數),這樣也更安全、更靈活。
- 修復建議與程式碼範例:
步驟 1: 建立 .env 檔案(此檔案絕對不能提交到版本控制)
# .env (放在專案根目錄)
DB_HOST=mysql.theaken.com
DB_PORT=33306
DB_USER=A101
DB_PASSWORD=您的新強密碼
DB_NAME=HBR_scraper
步驟 2: 建立 .gitignore 檔案(如果未來要使用 Git)
# .gitignore
.env
*.env
.env.*
__pycache__/
*.pyc
.venv/
venv/
hbr_articles.csv
*.log
步驟 3: 安裝 python-dotenv
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
# 修改前 (危險!)
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() 函數
# 修改前 (危險!)
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
# 修改前 (危險!)
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: 立即更換資料庫密碼
由於密碼已經暴露在原始碼中,即使您現在修復了程式碼,原來的密碼可能已經被他人知道。請:
- 登入 MySQL 伺服器
- 更換 A101 用戶的密碼為強密碼(至少 16 字元,包含大小寫字母、數字、特殊符號)
- 更新
.env檔案使用新密碼 - 更新 GitHub Actions secrets
-- 在 MySQL 中更換密碼
ALTER USER 'A101'@'%' IDENTIFIED BY '新的強密碼';
FLUSH PRIVILEGES;
🔴 HIGH-002: 弱密碼 - 資料庫密碼強度不足
- 風險等級:
High - 威脅描述: 目前使用的密碼
Aa123456是一個極其常見的弱密碼,在大多數密碼字典攻擊中會在前 100 個嘗試內被破解。 - 受影響組件: MySQL 用戶
A101
駭客劇本 (Hacker's Playbook):
「即使我沒有看到原始碼,我知道
mysql.theaken.com:33306是一個公開的 MySQL 伺服器。我啟動 Hydra(一個密碼暴力破解工具),使用常見密碼字典:hydra -l A101 -P /usr/share/wordlists/rockyou.txt mysql://mysql.theaken.com:33306不到一分鐘,工具告訴我密碼是
Aa123456。這個密碼太常見了,幾乎每個密碼字典都有它。」
- 修復建議:
- 使用密碼管理器生成強密碼(建議 20+ 字元)
- 密碼應包含:
- 大寫字母 (A-Z)
- 小寫字母 (a-z)
- 數字 (0-9)
- 特殊符號 (!@#$%^&*)
- 避免使用:
- 字典單詞
- 常見模式(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出現在列表中。這個伺服器對全世界開放,任何人都可以嘗試連線。我可以:
- 使用暴力破解工具嘗試常見用戶名/密碼組合
- 嘗試已知的 MySQL 漏洞
- 進行 DoS 攻擊使服務不可用」
- 修復建議:
方案 A: 設定防火牆白名單(推薦)
# 在伺服器上設定 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 隧道
# 不直接暴露 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 限制
-- 限制 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 檔案:
# 環境變數(最重要!)
.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 行)
# 危險的程式碼
cursor.execute(f"CREATE DATABASE IF NOT EXISTS `{database_name}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci")
雖然使用了反引號包裹,但如果 database_name 包含特殊字元(如反引號本身),仍可能導致 SQL 注入。
- 修復建議:
# 修改後 - 驗證資料庫名稱
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 行) -
修復建議:
# 修改前
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)
-
修復建議:
# 修改前 - 可能洩漏敏感資訊
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 -
修復建議:
# 使用連線池
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:
pip install DBUtils
🟢 LOW-002: GitHub Actions 工作流程安全性可加強
-
風險等級:
Low -
威脅描述: GitHub Actions 工作流程沒有明確限制權限,使用預設權限可能過於寬鬆。
-
受影響組件:
.github/workflows/weekly.yml -
修復建議:
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 -
修復建議:
# 在 pipelines.py 中加入資料清理
import html
import re
def sanitize_html(text: str) -> str:
"""移除或轉義 HTML 標籤"""
if not text:
return ''
# 移除 script 標籤
text = re.sub(r'<script[^>]*>.*?</script>', '', 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:
# .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. 資料庫查詢
- 繼續使用參數化查詢
- 實作查詢結果分頁(防止大量資料洩漏)
- 限制返回欄位(不返回敏感欄位)
修復優先順序
🔴 立即修復 (今天就要做)
- HIGH-001: 從所有原始碼中移除硬編碼的密碼,改用環境變數
- HIGH-002: 更換資料庫密碼為強密碼
- HIGH-003: 設定資料庫存取限制(IP 白名單或 VPN)
🟠 本週修復
- MEDIUM-001: 建立 .gitignore 檔案
- MEDIUM-002: 修復
create_databaseSQL 注入風險 - MEDIUM-003: 啟用資料庫 SSL/TLS 連線
- MEDIUM-004: 改善錯誤處理,避免資訊洩漏
🟢 可排程修復
- LOW-001: 實作資料庫連線池
- LOW-002: 加強 GitHub Actions 安全設定
- LOW-003: 加入輸入驗證和資料清理
結語
本次審計發現了幾個需要立即修復的嚴重安全問題,最關鍵的是 資料庫密碼硬編碼 和 弱密碼 問題。由於資料庫伺服器可從公網訪問,這些問題的風險被大幅放大。
好消息是,您的程式碼在其他方面相對安全:
- ✅ 使用參數化查詢(大部分情況)
- ✅ 遵守 robots.txt
- ✅ 使用 Gmail App Password 而非一般密碼
- ✅ 合理的程式碼結構
請按照優先順序儘快修復這些問題,特別是在將專案部署到生產環境之前。
報告結束
如有任何問題,請隨時詢問。