Vibe Coder

API 安全與 Secret 管理:別讓一組 API Key 拖垮帳單

API Key 絕對不能寫死在程式碼或放進前端,正確做法是存在環境變數,正式環境用平台的 Secrets 功能。JWT 要設過期時間並用至少 256 位元的隨機 Secret。如果 Key 不小心外洩,第一步永遠是立刻在服務端撤銷,再處理 git history 與異常使用紀錄。
API 安全與 Secret 管理:別讓一組 API Key 拖垮帳單

API 安全與 Secret 管理:別讓一組 API Key 拖垮帳單

有人專門在 GitHub 上掃描公開 repo 裡的 API Key,找到後幾秒內就開始盜用,OpenAI Key 洩漏後帳單暴增的故事並不少見。AI 生成的範例程式碼常直接寫 api_key="sk-xxx",那只是示範用,絕對不能照搬到真實專案。

這篇帶你看懂 API Key 該存在哪裡、.env 有哪些常見地雷、JWT 怎麼設計才安全,以及最重要的:一旦 Key 洩漏,該怎麼在最短時間內止血。

你將學到什麼

Key 該存在哪裡

環境變數、平台 Secrets、Secret Manager 的分工。

.env 的地雷

.gitignore 一定要加、絕不能寫死在程式碼裡。

JWT 怎麼設計才安全

過期時間、Secret 強度、正確的產生與驗證方式。

授權不能只靠前端

resource owner 驗證,防止 A 看到 B 的資料。

API Key 的正確存放方式

前端 ClientHeader 帶 API Key後端驗證.env 檔案 不進 git✕ Key 寫死在前端
Key 只活在後端的 env 檔案裡,前端程式碼裡不該出現它。
方式安全性適合
環境變數(.env)本機開發用,.env 加進 .gitignore本機開發
平台 SecretsRailway、Render、Vercel 的 Environment Variables正式環境部署
Secret ManagerAWS Secrets Manager、GCP Secret Manager企業級,有稽核
寫死在程式碼最不安全永遠不要
存在前端程式碼瀏覽器可以看到永遠不要

環境變數與 .env 的注意事項

# 這個檔案要加到 .gitignore,永遠不要 commit
.env
.env.local
.env.production

# 可以 commit 的:範本(不含真實值)
.env.example  # 內容只有 KEY_NAME= 沒有值
import os
from dotenv import load_dotenv

load_dotenv()  # 讀取 .env 檔案

# 用 os.getenv 讀取,不要寫死
API_KEY = os.getenv("OPENAI_API_KEY")
if not API_KEY:
    raise ValueError("OPENAI_API_KEY 環境變數未設定")
已經不小心 commit 了 Key 怎麼辦① 立刻撤銷那個 Key(在各平台的設定裡)② 產生新的 Key ③ 用 git filter-repo 或 BFG Repo Cleaner 清除 git history ④ 確認有沒有異常使用記錄。步驟①是最重要的,先撤銷再處理 history。

JWT 安全設計

JWT 是常見的 API 認證機制,但有幾個安全要點容易被忽略。

from datetime import datetime, timedelta
import jwt, os

SECRET = os.getenv("JWT_SECRET")  # 至少 256 位元的隨機字串

def create_token(user_id: str) -> str:
    payload = {
        "sub": user_id,
        "exp": datetime.utcnow() + timedelta(hours=1),  # 1 小時後過期
        "iat": datetime.utcnow(),  # 發行時間
    }
    return jwt.encode(payload, SECRET, algorithm="HS256")

def verify_token(token: str) -> dict:
    return jwt.decode(token, SECRET, algorithms=["HS256"])
JWT Secret 的強度JWT Secret 至少要 256 位元(32 bytes),用 openssl rand -hex 32 或 secrets.token_hex(32) 生成,不要用「secret」或「mysecret」這種弱字串。

API 授權:誰能做什麼

認證(Authentication)確認你是誰,授權(Authorization)確認你能做什麼,兩者都要做。

常見授權問題後果解法
沒有驗證 resource owner使用者 A 能讀取使用者 B 的資料查詢時帶入 current_user.id 過濾
前端隱藏按鈕就算授權直接呼叫 API 就能繞過後端每個 endpoint 都要驗證權限
Admin 功能沒有角色檢查一般使用者能呼叫管理員 API加 require_admin 的 middleware
async def get_my_weather_history(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    # 只取目前使用者的資料,不是全部
    return db.query(WeatherHistory).filter(
        WeatherHistory.user_id == current_user.id
    ).all()

Secret 洩漏的緊急處理

  1. 立刻撤銷:在各服務的設定頁面撤銷洩漏的 Key,這是最優先的事。
  2. 產生新的 Key:撤銷後立刻產生新 Key,更新所有用到的地方。
  3. 檢查異常使用:查看各服務的 usage log,確認有沒有未授權的使用。
  4. 清除 git history:用 git filter-repo 移除 commit 裡的 Secret,然後 force push。

延伸學習

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

常見問答

API Key 可以存在前端程式碼嗎?
絕對不行。前端程式碼在瀏覽器裡可以被任何人看到,Key 只要出現在前端就等於公開了。所有需要 Key 的呼叫都應該經過你自己的後端。
已經不小心 commit 了 Key 怎麼辦?
第一步永遠是立刻撤銷那個 Key,然後產生新 Key,接著用 git filter-repo 或 BFG Repo Cleaner 清除 git history,最後確認有沒有異常使用記錄。撤銷是最重要的一步,先撤銷再處理 history。
JWT 和一般的 Session 有什麼不同,安全設計要注意什麼?
JWT 是自帶資訊的加密字串,重點是要設定合理的過期時間(例如 1 小時),Secret 至少要 256 位元的隨機字串,不要用像 secret 這種弱字串。
什麼是 IDOR?
IDOR(Insecure Direct Object Reference)是指 API 沒有驗證資源的擁有者,例如 GET /api/history/123 只要沒確認 123 是不是你的資料,任何登入使用者都能看到別人的資料,這是很常見卻很容易被忽略的授權漏洞。