Python

資料安全與防護:AI 預設不安全,你才是最後一道防線

AI 生成的程式碼預設假設輸入乾淨、環境可信,不會主動加上安全防護。你需要補上五個原則:金鑰放進 .env 用 os.getenv() 讀取、SQL 一律參數化查詢、使用者輸入做型別與格式驗證、密碼一律用 bcrypt 雜湊儲存、錯誤訊息不外洩系統細節。
資料安全與防護: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 永遠參數化,不拼接;③ 使用者輸入永遠驗證,不信任;④ 密碼永遠雜湊,不明文存;⑤ 例外永遠記錄,不靜默吞。
AI 初版程式預設不安全① 金鑰進 .env,不進程式碼② SQL 一律參數化,不拼接③ 使用者輸入一律驗證,不信任④ 密碼一律雜湊,不明文存⑤ 例外一律記錄,不靜默吞你=最後一道防線補齊五道防線才能安全上線
AI 生成的初版程式預設不安全,你要補上五道防線,才能安全上線。

金鑰管理:.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.example 才是可以上傳的.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]   # 限制長度
給 AI 的輸入驗證提示詞「請在所有接收外部輸入的函式加上驗證:① 型別檢查;② 範圍或格式檢查;③ 長度限制;④ 特殊字元清洗。驗證失敗拋出有意義的 ValueError,加中文說明。」

密碼安全: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")
    )
Vibe Coder 觀察重點看到 AI 寫的登入系統,確認:① 密碼有沒有用 bcryptargon2 雜湊;② 比對時用 checkpw 而不是 ==== 有計時攻擊風險)。

AI 常見安全陷阱彙整

漏洞AI 常見寫法正確防護
API Key 洩漏硬寫在程式碼裡.envos.getenv()
SQL Injection字串拼接 SQL參數化查詢,用問號佔位符
路徑穿越直接用使用者輸入當檔案路徑sanitize_filename() 清洗
明文密碼直接存入資料庫bcrypt.hashpw() 雜湊
未驗證輸入假設格式正確直接處理型別、範圍、格式三層驗證
例外訊息洩漏return str(e) 直接給使用者記錄詳細錯誤,給使用者模糊訊息
給 AI 的安全審查提示詞「請對以下程式碼進行安全審查,重點檢查:① 金鑰是否用環境變數;② SQL 是否參數化;③ 使用者輸入是否有驗證;④ 密碼是否有雜湊;⑤ 例外訊息是否洩漏敏感資訊。列出每個問題和修正方式。」

三道遞進題:讀懂、改寫、抓錯

讀懂:追蹤安全措施如何串連運作;改寫:把不安全的初版升級到生產標準;抓錯:多重安全漏洞同時存在,練習一次找齊。

題目一・讀懂(基礎)

說明以下程式的安全防護機制:

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 而不是直接存密碼。

答案拆解① 三層防護:環境變數管理 SECRET(防金鑰洩漏)、使用者名稱格式驗證(防特殊字元注入)、密碼最短長度(防弱密碼)。
② 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()
給 AI 的提示詞「請對 login 函式做安全加固:① SQL 改成參數化查詢;② 密碼用 bcrypt.checkpw 驗證(不是字串比較);③ 加 with 管理連線;④ 加輸入驗證(使用者名稱不能空、長度限制);⑤ 加 sqlite3.Error 例外處理;⑥ 加中文註解。」
答案拆解① 參數化: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 / 密碼.envos.getenv() ,絕不硬寫
SQL Injection參數化查詢(問號佔位符),絕不拼接
惡意輸入型別、範圍、格式、長度四層驗證
密碼洩漏bcrypt.hashpw() 雜湊,絕不明文存
錯誤資訊外洩記錄詳細日誌,給使用者模糊訊息
Vibe Coder 安全五問金鑰在 .env 嗎?SQL 參數化了嗎?輸入驗證了嗎?密碼雜湊了嗎?錯誤訊息安全嗎?
  • 能用 .env + python-dotenv 安全管理所有敏感設定
  • 能對使用者輸入做型別、範圍、格式、長度四層驗證
  • 知道密碼必須用 bcrypt 雜湊,不能明文存
  • 能辨識並修正 SQL Injection、明文密碼、硬寫金鑰三大漏洞
  • 完成作業題目一:說明三層安全防護機制和 bcrypt 的作用
  • 完成作業題目二:請 AI 對登入函式做全面安全加固
  • 完成作業題目三:找出五個安全問題並說明危險原因

延伸學習

把這篇文章分享給需要的人FacebookLINEThreadsX

常見問答

為什麼不能把 API Key 直接寫在程式碼裡?
寫進程式碼等於寫進 git 歷史,就算之後刪除也無法真正清除,一旦上傳到公開 repo 就可能被機器人掃描濫用。正確做法是放進 .env 檔案並加入 .gitignore,程式改用 os.getenv() 讀取。
SQL Injection 是什麼?為什麼字串拼接 SQL 這麼危險?
把使用者輸入直接拼進 SQL 字串,攻擊者只要在輸入欄位加特殊符號,就能改變整條查詢的邏輯,例如繞過登入驗證或清空資料表。改用參數化查詢(問號佔位符)可以徹底避免這個問題。
密碼一定要用 bcrypt 嗎?可以自己寫雜湊邏輯嗎?
建議用 bcrypt 或 argon2 這類專為密碼設計的雜湊套件。它們會自動加入隨機 salt,讓相同密碼每次雜湊結果都不同,能有效防止彩虹表攻擊,自己土法煉鋼很容易漏掉這些細節。
AI 生成的程式碼為什麼常常不安全?
AI 傾向產生看起來能動的最短路徑程式,預設假設輸入乾淨、環境可信,不會主動加上驗證與防護。你可以用安全審查提示詞,要求 AI 針對金鑰、SQL、輸入驗證、密碼雜湊、錯誤訊息五個面向重新檢查。
程式出錯時,可以把完整錯誤訊息顯示給使用者嗎?
不建議。詳細錯誤內容(例如資料庫欄位名稱、檔案路徑)應該寫進日誌讓開發者查閱,回傳給使用者的應該是模糊、不洩漏系統細節的提示訊息。