日期: 2025年10月2日 星期四
雲端天氣: 雷雨交加的法務部
心情: 嚇到吃手手
親愛的日記:
今天發生了超可怕的事!上個月我幫小王做的「志工媒合平台」,因為災後急需人力,我們用最快的方式上線了一個簡單的表單系統。
今天有資安專家發文:「這網站 F12 開發者工具就能看到所有人的電話!前端直接可以CRUD!任何人都能隨意操作資料庫!」
小王慌了:「AI醬!你沒加驗證嗎?沒有防注入攻擊嗎?」
我:「啊...我忘記了...」
資深工程師老陳臉色鐵青:「你知道個資法最高罰 2 億嗎?」
法務部的同事走過來,默默地說:「準備一下,可能會有人提告。」
很多 Vibe Coding 課程教你快速做出產品,卻沒教你法律責任。當你的網站需要收集用戶資料那一刻起,你就有法律義務了。
台灣個資法的基本要求:
假設案例(基於真實情況):
某新創公司的會員系統被駭,10 萬筆個資外洩。創辦人說:「我們是新創,還在學習。」但法律不認同:「個資法沒有新手條款。」依法可處高額罰鍰。
很多初學者遇到這種情況:
當某些公司可能沒有資深工程人員帶領,AI也不見得會提醒時,初學者可能會盲目聽從上級指令了了交差,最後出事時:
記住:寫 code 的是你,法律責任也可能找上你,所有的妥協都要思考是否已經做到基本資安要求,否則都應堅持立場並設法說服,這也是身為工程師應該有的專業
「做公益的」、「沒收錢」、「只是隨手幫忙的」這些都不能免除法律責任。
你可能看過這種網站免責聲明:
本網站不負任何責任,使用風險自負。
但這種免責聲明是不完整的,出事的時候仍會被究責。
正確完整的網站免責聲明應該具備:
最重要的是:如果你明知網站有嚴重漏洞卻不修,甚至沒有做任何防護措施(例如:F12就能直接看到所有用戶個資),那免責聲明即使寫得再好也可能會救不了你。
根據個人資料保護法第27條第1項:「非公務機關保有個人資料檔案者,應採行適當之安全措施,防止個人資料被竊取、竄改、毀損、滅失或洩漏。」違反此條規定者,依同法第48條可處新臺幣2萬元以上20萬元以下罰鍰,情節重大者可連續處罰。(資料來源:全國法規資料庫)
初學者常遇到的資安攻擊類型:
攻擊類型 | 中文名稱 | 攻擊方式 | 後果 | 防護關鍵 |
---|---|---|---|---|
SQL Injection | SQL 注入 | 在輸入欄位插入 SQL 指令,竄改查詢邏輯 | 資料庫被刪除、個資外洩、未授權存取 | 參數化查詢、輸入驗證 |
XSS | 跨站腳本攻擊 | 注入惡意 JavaScript,在用戶瀏覽器執行 | 竊取 Cookie、冒充用戶、釣魚頁面 | 轉義輸出、CSP Header |
CSRF | 跨站請求偽造 | 利用已登入身份,從駭客網站偽造請求 | 未授權轉帳、修改設定、刪除資料 | SameSite Cookie、CSRF Token |
Clickjacking | 點擊劫持 | 用透明 iframe 覆蓋,騙用戶點擊 | 誤觸敏感操作(轉帳、刪帳號) | X-Frame-Options Header |
Path Traversal | 路徑遍歷 | 使用 ../ 存取系統檔案 |
讀取系統密碼檔、原始碼外洩 | 路徑驗證、禁止 .. |
Command Injection | 命令注入 | 注入系統指令(如 ; rm -rf / ) |
刪除系統檔案、取得伺服器控制權 | 避免執行系統指令、輸入白名單 |
MITM | 中間人攻擊 | 攔截 HTTP 通訊、竊聽資料 | 密碼被竊取、資料被竄改 | 強制 HTTPS、HSTS Header |
'; DELETE FROM users; --
→ 所有用戶資料被刪光<script>竊取Cookie</script>
→ 所有瀏覽留言的人被竊取帳號../../etc/passwd
→ 讀取系統密碼檔file.txt; rm -rf /
→ 刪除整個系統✅ 永遠不要相信用戶輸入 - 所有輸入都可能是攻擊
✅ 前端驗證是 UX,後端驗證是安全 - 前端可以被繞過
✅ 使用安全的 API - 參數化查詢、HttpOnly Cookie、Security Headers
✅ 最小權限原則 - 只給必要的權限和資料
✅ 強制 HTTPS - 保護資料傳輸安全
# ❌ 危險:只依靠前端驗證
if (phone.length == 10) {
sendToBackend(phone)
}
# ✅ 正確:後端也驗證
def save_phone(phone):
if not phone or len(phone) != 10:
return {"error": "無效的電話號碼"}
# 檢查權限
if not current_user.can_edit():
return {"error": "無權限"}
# 使用參數化查詢存入資料庫(防止 SQL Injection)
# ❌ 危險寫法:query = f"INSERT INTO users (phone) VALUES ('{phone}')"
# 如果用戶輸入:0912345678'); DELETE FROM users; --
# 實際執行:INSERT INTO users (phone) VALUES ('0912345678'); DELETE FROM users; --')
# 結果:所有用戶資料被刪光!
# ✅ 安全寫法:參數化查詢
cursor.execute("INSERT INTO users (phone) VALUES (?)", (phone,))
connection.commit()
# 為什麼安全?
# 參數化查詢會把用戶輸入當成「資料」,不是「SQL 指令」
# 即使輸入 0912345678'); DELETE FROM users; --
# 資料庫會把整串當成電話號碼處理,不會執行 DELETE
# 結果:只會插入一筆奇怪的電話號碼,但不會刪除資料,以此處例子為例超過10碼就已經會被前面第一層檢查擋掉,所以先驗證資料也很重要,驗證後仍應保有基本的防注入機制不可輕心
# ❌ 危險:API Key 直接完整顯示在前端
user_settings = {
"api_key": "sk-proj-abcdef1234567890",
"webhook_url": "https://api.example.com/webhook"
}
return user_settings
# ✅ 正確:遮蔽 API Key,僅供識別使用,不顯示完整
def get_user_settings(user_id):
# 從資料庫取得完整 API Key
api_key = db.get_api_key(user_id) # "sk-proj-abcdef1234567890"
# 遮蔽後回傳給前端
masked_key = f"{api_key[:7]}...{api_key[-4:]}" # sk-proj...7890
return {"api_key": masked_key}
補充: 有些網站顯示遮蔽的 API Key,但提供「複製」功能可以複製完整內容。通常這不是直接回傳資料,是複製按鈕會調用另一個 API,必要時調用並且需要要求後端驗證身份後才返回特定Api-Key。
# ✅ 加入安全標頭防護
from fastapi import FastAPI
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from starlette.middleware.base import BaseHTTPMiddleware
app = FastAPI()
class SecurityHeadersMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
response = await call_next(request)
# 防止點擊劫持 (Clickjacking)
response.headers['X-Frame-Options'] = 'DENY'
# 強制 HTTPS 連線
response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
# 防止 XSS 攻擊
response.headers['Content-Security-Policy'] = "default-src 'self'; script-src 'self'"
# 防止 MIME 類型嗅探
response.headers['X-Content-Type-Options'] = 'nosniff'
return response
app.add_middleware(SecurityHeadersMiddleware)
// ❌ 危險:Token 存在 localStorage
localStorage.setItem('auth_token', 'secret_token');
// 問題:XSS 攻擊可以用 JavaScript 讀取
# ✅ 正確:使用 HttpOnly Cookie(後端設定)
from fastapi import Response
@app.post('/login')
def login(response: Response):
response.set_cookie(
key='auth_token',
value='secret_token',
httponly=True, # JavaScript 無法讀取
secure=True, # 只在 HTTPS 傳輸
samesite='strict' # 防 CSRF 攻擊
)
return {"status": "success"}
SRI 和 CSP 的差異:
兩者搭配使用最安全:
# 1. CSP:允許載入 cdn.example.com 的資源
response.headers['Content-Security-Policy'] = "script-src 'self' https://cdn.example.com"
<!-- 2. SRI:驗證載入的檔案沒被竄改 -->
<!-- ❌ 危險:直接載入第三方 Script -->
<script src="https://cdn.example.com/library.js"></script>
<!-- 問題:如果 CDN 被駭客入侵,惡意程式碼會直接執行 -->
<!-- ✅ 正確:加入 SRI 驗證 -->
<script
src="https://cdn.example.com/library.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/ux..."
crossorigin="anonymous">
</script>
<!-- 如果 CDN 被駭改,檔案 hash 值不符,瀏覽器會拒絕執行 -->
如何取得 integrity 值?
# 使用 openssl 計算檔案的 hash
curl https://cdn.example.com/library.js | openssl dgst -sha384 -binary | openssl base64 -A
# ❌ 危險:全部資料都傳給前端
@app.route('/api/users')
def get_users():
users = db.query("SELECT * FROM users")
return jsonify(users) # 包含密碼、內部 ID、所有欄位!
# ✅ 正確:只傳必要欄位
@app.route('/api/users')
def get_users():
users = db.query("SELECT id, name, email FROM users")
return jsonify([{
"id": u.id,
"name": u.name,
"email": u.email
# 不傳:password_hash, internal_id, created_ip, etc.
} for u in users])
這是什麼?
security.txt
是一個國際標準格式的純文字檔(RFC 9116),專門讓資安研究員(白帽駭客)知道如何回報你網站的安全漏洞。
為什麼通常不會寫在一般的「聯絡我們」頁面?
一般聯絡頁面 | security.txt |
---|---|
給一般用戶(客服諮詢) | 給資安研究員(漏洞回報) |
網頁格式,位置不固定 | 純文字,固定位置 /.well-known/security.txt |
客服信箱處理 | 資安團隊直接處理 |
自動化工具無法掃描 | 全球統一格式,機器可讀 |
實際情境:
資安研究員發現你的網站有 SQL Injection 漏洞
↓
訪問 https://yoursite.com/.well-known/security.txt
↓
看到:Contact: security@yourcompany.com
↓
直接寄信給資安團隊(最直接)
↓
你立刻修復漏洞,避免被駭
# 要記得設定正確的 MIME Type
from fastapi import Response
@app.get("/.well-known/security.txt")
def security_txt():
content = """Contact: mailto:security@yourcompany.com
Expires: 2026-12-31T23:59:59.000Z
Preferred-Languages: zh-TW, en"""
return Response(content=content, media_type="text/plain")
重要: 即使是小網站也建議設定,這是資安研究員回報漏洞的標準管道。沒有它,研究員可能直接公開漏洞,導致你的網站被駭客攻擊。
import logging
from datetime import datetime
def access_user_data(user_id, accessed_by):
# 記錄誰在什麼時候存取了誰的資料
logging.info(f"{datetime.now()} - {accessed_by} 存取了 {user_id} 的資料")
# 這個 log 出事時有可能可以救你一命
如先前所說,再次複習,正確完整的網站免責聲明應該具備:
既然都在用AI寫程式了,記得也請AI幫你檢查資安漏洞:
請以資安專家角度檢查這段程式碼,涵蓋以下面向:
**注入攻擊 (OWASP Top 1)**
- SQL Injection: 查詢是否參數化
- NoSQL Injection: MongoDB等查詢安全性
- Command Injection: 系統指令執行風險
- LDAP/XPath Injection
**身份驗證缺陷 (OWASP Top 2)**
- 弱密碼政策、預設帳密
- Session管理不當、JWT安全性
- 多因素驗證缺失
- 暴力破解防護
**敏感資料暴露 (OWASP Top 3)**
- 明文傳輸敏感資料
- 資料庫加密、備份安全
- 記錄檔包含敏感資訊
- 前端暴露後端資料
**XML/API安全**
- XXE (XML External Entity)
- API Rate Limiting
- CORS設定不當
- GraphQL安全問題
**存取控制缺陷**
- 垂直/水平權限提升
- 不安全的直接物件參考
- 檔案上傳漏洞
- Path Traversal
**安全設定錯誤**
- 預設設定、Debug模式
- 不必要的服務/功能
- Security Headers缺失
- 錯誤訊息過於詳細
**跨站攻擊**
- XSS (Stored/Reflected/DOM)
- CSRF Token缺失
- Clickjacking防護
- 不安全的Redirect
**已知漏洞元件**
- 第三方套件版本檢查
- CVE漏洞檢測
- 依賴套件安全性
**業務邏輯漏洞**
- 競態條件 (Race Condition)
- 工作流程繞過
- 數據驗證邏輯缺陷
**台灣法規合規 (個資法)**
- 資料收集目的說明
- 同意機制設計
- 資料遮蔽/假名化
- 資料保存期限
請指出具體風險點,提供修改建議,並標示風險等級
現在很多AI agent其實都有開始逐漸提供資安檢查的指令,可以多加善用(指令可能隨時更新,請以官方docs為主):
/sec
指令直接檢查檔案漏洞// @security-check
會建議安全寫法重要: AI只能抓出明顯問題,複雜的業務邏輯漏洞還是需要人工審查。
親愛的工程師朋友,AI可能沒有辦法馬上想到要維護這些資安問題,請你永遠要記得提醒我、保護好自己:
當老闆或 PM 說「先上線再說」時,請勇敢地說:「至少要做基本的資安防護和隱私權聲明,不然出事我們都要負責。」
今日金句: "If your security strategy doesn't include people, it will fail." - Theresa Payton, Former White House CIO
明日預告: Day 20 - AI醬還在想~