Python

合規與資料保護:讓合規不是限制,而是你的護身符

合規與資料保護,指的是在寫程式的當下就落實個資法與 GDPR 的蒐集、告知、刪除、加密與稽核要求,而不是等出事才補救。最基本的四件事是:只蒐集真正需要的欄位、提供刪除個資的功能、用 Fernet 加密敏感欄位,並且把每一次操作都寫進稽核日誌,記錄誰在何時做了什麼。
合規與資料保護:讓合規不是限制,而是你的護身符

合規與資料保護:讓合規不是限制,而是你的護身符

你有沒有想過,你寫的程式裡,那張存著用戶 email 和電話的資料表,萬一哪天被駭客整包搬走,會發生什麼事?合規不是法務部門的事,是你在下第一行程式碼之前就該想清楚的設計問題。事後補合規,代價永遠比一開始就設計好高出許多。

這一課帶你把台灣個資法、GDPR、稽核日誌和資料加密這幾件事,變成你寫程式時的直覺反應:蒐集資料前先問「這個欄位真的需要嗎」,寫入資料庫前先問「這個操作有沒有留下紀錄」,存敏感欄位前先問「這個要不要加密」。

你將學到什麼

台灣個資法五大義務

蒐集目的、告知義務、當事人權利、安全維護、保存期限,每一項都對應到程式裡的具體要求。

GDPR 六大原則

有歐洲用戶就得懂的規則,一張表看懂合法性、最小化、儲存限制怎麼落地成程式設計。

稽核日誌怎麼寫

用 Python logging 記錄誰在何時做了什麼,出事時才有東西可以還原真相。

用 Fernet 加密敏感欄位

資料庫備份被偷、硬碟遺失,加密能讓這些事件不會演變成個資外洩事件。

合規不是限制,是你的護身符

收集台灣用戶個資,你就受個資法約束;有歐洲用戶,你就受 GDPR 約束。法規不是選配項目,而違反法規的罰款,遠遠超過把合規設計進架構的開發成本。這一課要讓你養成四個習慣:蒐集個資前先確認目的、寫入操作前先留下稽核紀錄、敏感欄位存進資料庫前先加密、資料保存到期就清理或匿名化。

  • 法規不是選配:收集台灣用戶個資受個資法約束,有歐洲用戶則受 GDPR 約束,違反的罰款遠超開發成本。
  • 合規要從架構設計開始:事後補合規,比從一開始就設計合規貴上許多;需求階段就該問「這些資料有沒有合規問題」。
  • 日誌稽核是必備工具:誰在什麼時間做了什麼?有完整的稽核日誌,你才能在出問題時還原事實。
  • 加密保護靜態資料:資料庫備份被盜、硬碟遺失,加密能讓這些事件不會演變成個資外洩事件。
蒐集資料(最小化原則)存入資料庫(Fernet 加密敏感欄位)存取與操作(寫入稽核日誌)每個環節都要能回答:這樣做合規嗎?最小化、加密、稽核,是把合規變成寫程式時的直覺反應
把合規變成直覺反應:蒐集想最小化,入庫想加密,操作想稽核。

9-1 台灣個資法基礎

《個人資料保護法》規範所有涉及個人資料的蒐集、處理、利用。開發任何收集用戶資料的系統前,你必須先問清楚下面這五件事,各自對應到程式裡具體要做到的地方。

義務說明AI 程式要求
蒐集目的蒐集前需指定目的,不能超範圍使用程式說明中標明資料用途
告知義務蒐集前必須告知當事人UI 必須有隱私政策連結
當事人權利查詢、更正、刪除、停止使用程式必須支援刪除操作
安全維護防止個資被竊取或洩漏加密、存取控制、稽核日誌
保存期限目的達成後應刪除或匿名化排程清理過期資料

給 AI 的個資保護提示詞

直接拿去用「在這個系統中,以下欄位屬於個人資料:name、email、phone。請確認:一、有刪除功能,支援當事人的刪除請求;二、敏感欄位加密存儲;三、所有存取操作都寫入稽核日誌,記錄誰、何時、做了什麼。」

9-2 GDPR 六大原則(簡明版)

歐盟 GDPR 適用於任何處理歐盟居民個資的組織,不論你的公司在哪個國家。只要產品有歐洲用戶,你就必須遵守。下面這張表把六大原則翻成程式實作上該做的事。

原則核心要求程式實作要點
合法性必須有合法依據才能處理個資(同意、合約、法律義務)同意記錄要存入資料庫
目的限制蒐集目的外不可另作他用資料欄位加目的標籤
最小化只蒐集必要的資料API 設計不回傳不必要欄位
正確性確保資料準確並可更新支援更新與版本紀錄
儲存限制過了保存期限要刪除或匿名化排程清理加上匿名化函式
完整性與保密性確保安全,防止未授權存取加密、存取控制、日誌
Vibe Coder 合規最小清單即使不需要完整 GDPR 合規,這三件事是最基本的:一、有隱私政策頁面;二、用戶可以刪除自己的資料;三、敏感欄位加密存儲。

9-3 日誌稽核:記錄誰在何時做了什麼

稽核日誌(Audit Log)是合規的核心工具,也是出問題時還原事實的唯一依據。所有涉及個資的操作,都應該寫入稽核日誌。下面示範一支獨立的稽核 logger,加上一個共用的 log_action() 函式。

import logging, datetime, json

# 設定獨立的稽核 logger
audit_logger = logging.getLogger("audit")
audit_handler = logging.FileHandler("audit.log", encoding="utf-8")
audit_logger.addHandler(audit_handler)
audit_logger.setLevel(logging.INFO)

def log_action(user_id: str, action: str, target: str,
               details: dict = None) -> None:
    """ 寫入一筆稽核記錄。 """
    record = {
        "timestamp": datetime.datetime.utcnow().isoformat() + "Z",
        "user_id":   user_id,
        "action":    action,   # CREATE / READ / UPDATE / DELETE
        "target":    target,   # 操作的資源,如 "user:42"
        "details":   details or {}
    }
    audit_logger.info(json.dumps(record, ensure_ascii=False))

# 使用範例
log_action("admin_01", "DELETE", "user:123",
           {"reason": " 用戶申請刪除帳號 "})
稽核日誌五要素時間戳記(UTC ISO 格式)、操作者(user_id)、動作(CRUD)、對象(資源 ID)、細節(原因、變更前後值),五個都齊全,日誌才有還原事實的能力。

9-4 資料加密:靜態加密與傳輸加密

靜態資料加密(Fernet 對稱加密)

Fernet 是 cryptography 套件提供的對稱加密方案,加密後可以解密還原成明文,適合用來保護資料庫裡的敏感欄位。金鑰要存進環境變數,絕對不要寫死在程式碼裡。

from cryptography.fernet import Fernet   # pip install cryptography
import os

# 一次性生成金鑰(存到 .env,不要寫死)
key = Fernet.generate_key()
cipher = Fernet(os.getenv("FERNET_KEY").encode())

# 加密
def encrypt(plaintext: str) -> str:
    return cipher.encrypt(plaintext.encode("utf-8")).decode()

# 解密
def decrypt(ciphertext: str) -> str:
    return cipher.decrypt(ciphertext.encode()).decode("utf-8")

# 範例:加密存入資料庫,讀取時解密
email_enc = encrypt("alice@example.com")
# cursor.execute("INSERT INTO users VALUES (?)", (email_enc,))
email_plain = decrypt(email_enc)
場景方法說明
資料庫敏感欄位Fernet 對稱加密應用層加密,加密後再存入
API 傳輸HTTPS(TLS)部署時配置,不需在程式碼處理
備份檔案AES 256 加密備份前加密,金鑰分開保管
密碼bcrypt 雜湊單向加密,無法解密還原

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

這三題分別練什麼讀懂:追蹤稽核日誌如何記錄每一個操作。改寫:加入加密和日誌,讓系統達到合規標準。抓錯:找出違反合規原則的設計。

題目一:讀懂(基礎)

說明以下稽核日誌系統的設計理念。

import logging, datetime, json

audit = logging.getLogger("audit")
audit.addHandler(logging.FileHandler("audit.log", encoding="utf-8"))
audit.setLevel(logging.INFO)

def log_action(uid, action, target, before=None, after=None):
    audit.info(json.dumps({
        "ts":     datetime.datetime.utcnow().isoformat() + "Z",
        "uid":    uid,
        "action": action,
        "target": target,
        "before": before,
        "after":  after
    }, ensure_ascii=False))

log_action("u001", "UPDATE", "user:42",
           before={"email": "old@x.com"},
           after ={"email": "new@x.com"})

任務:說明為什麼要用 UTC 時間、為什麼記錄 before 和 after、以及 ensure_ascii=False 的作用。

答案拆解一、UTC 時間能消除時區混淆:跨國系統的日誌如果用本地時間,不同時區的記錄就無法對照排序。
二、記錄 before 和 after,讓稽核人員能看到「什麼被改了」,是追蹤資料變更的關鍵,也是還原資料的依據。
三、ensure_ascii=False 讓 JSON 輸出直接顯示中文,而不是 \uXXXX 跳脫序列,日誌可讀性大幅提升。

題目二:改寫(進階)

合規不只是加程式碼,是設計哲學合規系統的核心,在於設計時就考慮資料的生命週期:資料從哪裡來、用在哪裡、保存多久、如何刪除。一個請求 AI 加入合規功能的提示詞,往往能讓系統的架構品質提升一個等級。

請 AI 把以下用戶管理系統升級為合規版本。

import sqlite3

def create_user(name, email):
    with sqlite3.connect("users.db") as conn:
        conn.execute(
            "INSERT INTO users (name, email) VALUES (?, ?)",
            (name, email)
        )
        conn.commit()
給 AI 的提示詞「請升級 create_user 函式,達到合規標準:一、email 欄位用 Fernet 加密存儲;二、新增 delete_user 函式,支援用戶刪除請求;三、兩個操作都寫入 audit.log,包含時間戳記、操作者、動作、對象;四、加入資料最小化說明,只蒐集必要欄位。」
答案拆解一、email 加密:cipher.encrypt(email.encode()) 後再存,讀取時解密,金鑰存在 .env
二、delete_user(user_id) 支援 GDPR 的被遺忘權:用 DELETE FROM users WHERE id 為問號佔位,並寫入稽核日誌。
三、每次操作呼叫 log_action(operator_id, "CREATE", f"user:{新 id}") 就能形成完整的操作軌跡。

題目三:抓錯(高階)

合規問題往往不是「程式會崩潰」,而是「程式能跑但違法」,這類錯誤在 code review 時最容易被忽略。

import sqlite3, logging

logging.basicConfig(level=logging.DEBUG)

def get_all_users():
    """ 取得所有用戶資料(供報表使用)。 """
    with sqlite3.connect("users.db") as conn:
        conn.row_factory = sqlite3.Row
        cur = conn.cursor()
        cur.execute("SELECT id, name, email, phone, ssid FROM users")
        return [dict(row) for row in cur.fetchall()]

# 呼叫後直接 print 到 log
users = get_all_users()
logging.debug(f" 報表資料:{users} ")

任務:找出四個合規問題,說明各自違反了哪個原則。

問題一SELECT 回傳 ssid(身分證字號),違反最小化原則:報表通常不需要 ssid,不應該查詢。
問題二logging.debug(users) 把完整個資(包含 email、phone、ssid)寫入 log 檔,違反保密原則。
問題三沒有存取控制:任何程式都能呼叫 get_all_users() 取得所有用戶資料,違反存取最小化。
問題四沒有稽核日誌:誰查詢了所有用戶資料、何時查詢,完全沒有記錄,出問題時無法追責。

術語速查表

術語說明
個資法台灣《個人資料保護法》,規範個資蒐集、處理、利用的基本法律。
GDPR歐盟《通用資料保護規則》,適用於處理歐盟居民個資的所有組織,不限所在地。
個人資料能直接或間接識別特定自然人的資訊,如姓名、Email、身分證字號、IP 位址等。
蒐集目的蒐集個資時必須聲明的用途,且不能超出此目的使用(目的限制原則)。
最小化原則只蒐集達成目的所必要的最少資料量,多餘欄位不應蒐集。
被遺忘權GDPR 賦予用戶要求刪除其個人資料的權利,系統必須提供刪除功能。
稽核日誌記錄所有資料操作的日誌,包含時間戳記、操作者、動作、對象。
UTC 時間協調世界時,稽核日誌應使用 UTC 避免時區混淆,格式為 ISO 8601。
FernetPython cryptography 套件提供的對稱加密方案,適合加密資料庫敏感欄位。
靜態加密對存儲中的資料加密,防止儲存媒介(硬碟、備份)被盜時資料外洩。
傳輸加密對網路傳輸中的資料加密,部署時由伺服器設定,不需在應用程式碼處理。
匿名化將個資移除或替換成無法識別個人的形式,匿名化後不再受個資法規範。

重點整理與完成清單

項目實作方式
蒐集告知隱私政策頁面加上同意記錄
目的限制API 只回傳必要欄位(最小化)
刪除權利delete_user(id) 函式
敏感欄位保護Fernet 加密存儲
操作可追溯稽核日誌(時間、人、動作、對象)
Vibe Coder 合規四問蒐集了哪些個資?有沒有做到最小化?有沒有刪除功能,回應被遺忘權?敏感欄位加密了嗎?操作有沒有寫入稽核日誌?寫程式前把這四題問過一輪,合規就不再是上線前才臨時補的東西。

模組九完成清單

  • 了解台灣個資法的五大義務:蒐集目的、告知、權利、安全、保存期限。
  • 了解 GDPR 六大原則,知道何時適用。
  • 能實作稽核日誌,包含 UTC 時間、操作者、動作、對象、前後值。
  • 能用 Fernet 加密資料庫敏感欄位。
  • 完成作業題目一:說明 UTC 時間、before/after、ensure_ascii 的作用。
  • 完成作業題目二:請 AI 升級系統,達到合規標準。
  • 完成作業題目三:找出最小化、保密、存取控制、稽核四個問題。

延伸學習

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

常見問答

沒有歐洲用戶,還需要懂 GDPR 嗎?
如果產品完全沒有歐洲用戶,嚴格來說不受 GDPR 約束。但它的六大原則,像最小化、儲存限制、完整性與保密性,本身就是良好的系統設計,直接拿來當作合規最低標準也很划算。
稽核日誌一定要記錄哪些欄位?
至少要有時間戳記(建議用 UTC)、操作者、動作類型(CRUD)、操作對象,以及異動細節。少了任何一項,出問題時都很難還原事實。
稽核日誌的時間為什麼要用 UTC,不能用本地時間?
跨國或跨時區的系統如果用本地時間記錄,不同地區的日誌就沒辦法直接對照排序。UTC 是唯一不會因時區而混淆的共同基準,這也是 ISO 8601 格式常搭配 UTC 使用的原因。
Fernet 加密和 bcrypt 雜湊有什麼不同?
Fernet 是對稱加密,可以解密還原成明文,適合 email、電話這類需要讀回原值的欄位。bcrypt 是單向雜湊,無法還原,只適合密碼這類只需要比對、不需要讀回原值的資料。
個資法要求的刪除功能,實作上要注意什麼?
至少要有一支真的會刪除資料的函式(不是只把狀態標記成已刪除卻仍保留明文),刪除動作本身也要寫入稽核日誌,才能證明你確實回應了當事人的請求。