Python
資料安全與防護:AI 預設不安全,你才是最後一道防線
資料安全與防護:AI 預設不安全,你才是最後一道防線
你請 AI 寫程式的時候,它最先想的通常是「能不能動」,不是「安不安全」。金鑰寫死在程式碼裡、SQL 用字串直接拼接、密碼原封不動存進資料庫,這些寫法在原型階段完全看不出問題,一旦上線就是真金白銀的損失,嚴重的話整個資料庫可能被清空。
AI 生成的初版程式預設不安全,這是你身為 Vibe Coder 最後一道把關的地方。這篇文章帶你補上五道最常被漏掉的防線:金鑰管理、輸入驗證、密碼安全,最後用三道遞進題(讀懂、改寫、抓錯)練習把「能用」的程式改寫成「能上線」的程式。
你將學到什麼
金鑰管理:.env 與環境變數
用 .env 檔案和 os.getenv() 安全管理 API Key 與資料庫密碼,不讓機密寫進程式碼與 git 歷史。
輸入驗證:不信任任何外部輸入
型別、範圍、格式、長度四層驗證,堵住 AI 初版程式常見的驗證漏洞。
密碼安全:bcrypt 雜湊存儲
用 bcrypt 雜湊密碼並以 checkpw() 驗證,資料庫外洩也不怕明文密碼曝光。
五大安全陷阱速查表
一次看懂金鑰洩漏、SQL Injection、路徑穿越等常見漏洞與正確防護寫法。
三道實戰練習
從讀懂安全機制、改寫不安全程式碼,到抓出藏在程式裡的多重安全問題。
為什麼 Vibe Coder 需要懂資料安全
安全漏洞會直接變成損失:API Key 外洩被濫用可能產生巨額費用,SQL Injection 可以讓攻擊者清空資料庫。企業的 AI 專案幾乎都有安全審查,你懂安全,交付物才過得了關。
.env,不進程式碼;② SQL 永遠參數化,不拼接;③ 使用者輸入永遠驗證,不信任;④ 密碼永遠雜湊,不明文存;⑤ 例外永遠記錄,不靜默吞。金鑰管理:.env 與環境變數
API Key、資料庫密碼、私密設定,所有敏感資訊都不能寫在程式碼裡。寫進程式碼就等於寫進 git 歷史,永遠無法真正刪除。
# .env 檔案(放在專案根目錄)
# OPENAI_API_KEY=sk-abc123
# DB_PASSWORD=super_secret
# DEBUG=False
# .gitignore(必須加上)
# .env
# Python 程式
from dotenv import load_dotenv
import os
load_dotenv() # 讀取 .env 檔到環境變數
api_key = os.getenv("OPENAI_API_KEY")
db_pass = os.getenv("DB_PASSWORD")
debug = os.getenv("DEBUG", "False") == "True"
# 啟動時驗證必要設定都存在
if not api_key:
raise EnvironmentError("OPENAI_API_KEY 未設定,請確認 .env 檔案")
.env 本身絕對不能進 git。要給團隊參考格式,另外準備一份範本檔:# .env.example(上傳 git,給團隊參考格式)
OPENAI_API_KEY=your_key_here
DB_PASSWORD=your_password_here
輸入驗證:永遠不信任使用者輸入
所有來自外部的資料(表單、API 請求、CSV 上傳)都必須驗證。AI 的初版程式通常假設輸入是正確的,你要主動要求它加上驗證。
import re
def validate_email(email: str) -> bool:
""" 驗證 email 格式。 """
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email))
def validate_age(age_str: str) -> int:
""" 驗證年齡為 0 到 150 的整數。 """
try:
age = int(age_str)
except ValueError:
raise ValueError(f" 年齡必須是整數,收到:{age_str}")
if not 0 <= age <= 150:
raise ValueError(f" 年齡超出合理範圍:{age}")
return age
def sanitize_filename(name: str) -> str:
""" 移除檔案名稱中的危險字元。 """
safe = re.sub(r'[^\w\-.]', '_', name)
return safe[:255] # 限制長度
密碼安全:bcrypt 雜湊存儲
密碼永遠不能明文存入資料庫。即使資料庫被盜,正確雜湊過的密碼也無法還原。AI 的初版有時候直接存明文,你必須要求修正。
user["password"] = password 直接存明文,資料庫一旦外洩就完蛋。import bcrypt # pip install bcrypt
# 註冊:雜湊後存入資料庫
def hash_password(password: str) -> str:
return bcrypt.hashpw(
password.encode("utf-8"),
bcrypt.gensalt()
).decode("utf-8")
# 登入:驗證輸入是否符合雜湊
def verify_password(password: str, hashed: str) -> bool:
return bcrypt.checkpw(
password.encode("utf-8"),
hashed.encode("utf-8")
)
bcrypt 或 argon2 雜湊;② 比對時用 checkpw 而不是 ==(== 有計時攻擊風險)。AI 常見安全陷阱彙整
| 漏洞 | AI 常見寫法 | 正確防護 |
|---|---|---|
| API Key 洩漏 | 硬寫在程式碼裡 | .env + os.getenv() |
| SQL Injection | 字串拼接 SQL | 參數化查詢,用問號佔位符 |
| 路徑穿越 | 直接用使用者輸入當檔案路徑 | sanitize_filename() 清洗 |
| 明文密碼 | 直接存入資料庫 | bcrypt.hashpw() 雜湊 |
| 未驗證輸入 | 假設格式正確直接處理 | 型別、範圍、格式三層驗證 |
| 例外訊息洩漏 | return str(e) 直接給使用者 | 記錄詳細錯誤,給使用者模糊訊息 |
三道遞進題:讀懂、改寫、抓錯
讀懂:追蹤安全措施如何串連運作;改寫:把不安全的初版升級到生產標準;抓錯:多重安全漏洞同時存在,練習一次找齊。
題目一・讀懂(基礎)
說明以下程式的安全防護機制:
import os, re, bcrypt
from dotenv import load_dotenv
load_dotenv()
SECRET = os.getenv("APP_SECRET")
if not SECRET:
raise EnvironmentError("APP_SECRET 未設定")
def register(username: str, password: str) -> dict:
if not re.match(r'^[a-zA-Z0-9_]{3,20}$', username):
raise ValueError(" 使用者名稱只能包含英數字和底線,長度 3 到 20")
if len(password) < 8:
raise ValueError(" 密碼至少 8 個字元")
hashed = bcrypt.hashpw(
password.encode("utf-8"), bcrypt.gensalt()
).decode("utf-8")
return {"username": username, "password": hashed}
任務:說明這段程式有幾層安全防護,以及為什麼要用 bcrypt 而不是直接存密碼。
② bcrypt 每次雜湊都加隨機 salt,相同密碼雜湊值不同,無法用彩虹表反查,資料庫外洩也不會直接洩漏明文密碼。
題目二・改寫(進階)
AI 給的初版程式通常「能用」但不安全,把它升級到生產等級的差距不大,但影響極深,一個安全問題可以讓整個系統的信任崩潰。請 AI 對以下程式做全面安全加固:
import sqlite3
def login(username, password):
conn = sqlite3.connect("users.db")
cur = conn.cursor()
sql = "SELECT * FROM users WHERE username='" \
+ username + "' AND password='" + password + "'"
cur.execute(sql)
return cur.fetchone()
cur.execute("... WHERE username = ?", (username,)) ,再用 bcrypt.checkpw 比對密碼雜湊。② 密碼絕對不能放進 SQL 的 WHERE 條件裡比對,必須先查到雜湊值,再用 checkpw 在 Python 端比對。
③ 輸入驗證:使用者名稱不能空、長度不能超過 50,避免空輸入或超長輸入拖垮系統。
題目三・抓錯(高階)
實際的安全漏洞通常是多個問題同時存在,而且常常藏在看起來沒問題的程式碼裡。找出以下程式碼的所有安全問題:
import os
DB_PASS = "admin123" # 問題一
API_KEY = os.environ["KEY"] # 問題二
def save_user(username, email, password):
if len(username) > 0: # 問題三(驗證不足)
conn.execute(
"INSERT INTO users VALUES ('"
+ username + "','" + email + "','"
+ password + "')" # 問題四、五
)
return True
任務:找出五個安全問題,說明危險原因。
| 問題 | 說明 |
|---|---|
| 問題一 | DB_PASS 硬寫在程式碼裡,應改用 os.getenv("DB_PASS") 從 .env 讀取。 |
| 問題二 | os.environ["KEY"] 在環境變數不存在時會拋出 KeyError 讓程式崩潰,應改用 os.getenv("KEY") 並加存在性檢查。 |
| 問題三 | 只檢查長度大於 0,沒有格式驗證,惡意字元、超長字串都可以進來。 |
| 問題四 | 字串拼接 SQL,有 SQL Injection 風險,應改用參數化查詢。 |
| 問題五 | password 明文存入資料庫,應先用 bcrypt 雜湊再存。 |
術語速查表
| 術語 | 說明 |
|---|---|
.env 檔案 | 存放敏感環境變數(API Key、密碼等)的純文字設定檔,必須加入 .gitignore。 |
os.getenv(key, default) | 安全讀取環境變數,不存在時回傳 default,不像 os.environ[] 會拋出 KeyError。 |
python-dotenv | 第三方套件,load_dotenv() 讀取 .env 檔並載入為環境變數。 |
| 輸入驗證 | 對外部輸入做型別、範圍、格式、長度四層檢查,驗證失敗應拋出有意義的 ValueError。 |
| 清洗(sanitize) | 移除或轉義輸入中的危險字元,防止注入攻擊。檔案名稱、HTML 輸出都需要清洗。 |
bcrypt | 密碼雜湊套件,hashpw() 雜湊、checkpw() 驗證,自動加 salt,不可逆。 |
| salt | 雜湊前加入的隨機值,讓相同密碼每次雜湊結果不同,防止彩虹表攻擊。 |
| 彩虹表攻擊 | 預先計算大量密碼雜湊值的對照表,用來反查密碼原文。加 salt 可有效防範。 |
| 最小權限原則 | 程式只申請完成任務必要的最小權限,減少洩漏或被攻擊時的損害範圍。 |
| 計時攻擊 | 透過比較密碼的時間差推測正確內容,bcrypt.checkpw() 內建常數時間比較防範此攻擊。 |
重點整理與完成清單
| 風險 | 正確做法 |
|---|---|
| API Key / 密碼 | .env + os.getenv() ,絕不硬寫 |
| SQL Injection | 參數化查詢(問號佔位符),絕不拼接 |
| 惡意輸入 | 型別、範圍、格式、長度四層驗證 |
| 密碼洩漏 | bcrypt.hashpw() 雜湊,絕不明文存 |
| 錯誤資訊外洩 | 記錄詳細日誌,給使用者模糊訊息 |
- 能用
.env+ python-dotenv 安全管理所有敏感設定 - 能對使用者輸入做型別、範圍、格式、長度四層驗證
- 知道密碼必須用 bcrypt 雜湊,不能明文存
- 能辨識並修正 SQL Injection、明文密碼、硬寫金鑰三大漏洞
- 完成作業題目一:說明三層安全防護機制和 bcrypt 的作用
- 完成作業題目二:請 AI 對登入函式做全面安全加固
- 完成作業題目三:找出五個安全問題並說明危險原因
延伸學習
HE201|Harness Engineering System Design(6 小時)
六小時的實作課:從 Blueprint 走到可以跑的規格,再用 No-code、n8n 低程式碼與程式碼三條路各做一次同一個 harness,最後處理可靠度——重試、錯誤處理、人工覆核。7 章 54 課,含常見坑與排錯、Capstone 實作,附學員講義 PDF。
NT$ 5,999
做出你的第一個 Skill
一場快閃直播的完整重製。從搞懂 Skill 的五個層級開始,帶你把一件你每天在做的重複工作,寫成一支 AI 真的會照做的 Skill ── 命名、description、輸入拆解、Workflow 訪談、Output 與 Checks,最後組成一份能通過格式檢查的 SKILL.md。
NT$ 999

