一、合规框架全景
AI 应用合规框架 │ ┌─────────────────┼─────────────────┐ │ │ │ ┌────▼────┐ ┌────▼────┐ ┌─────▼─────┐ │ 数据保护 │ │ 模型治理 │ │ 运营合规 │ └──────────┘ └──────────┘ └───────────┘ │ │ │ ┌────▼────┐ ┌────▼────┐ ┌─────▼─────┐ │ GDPR │ │ 公平性 │ │ 审计日志 │ │ 个保法 │ │ 可解释性 │ │ 风险评估 │ │ CCPA │ │ 鲁棒性 │ │ 第三方审计 │ │ PIPL │ │ 透明度 │ │ 合规报告 │ └──────────┘ └──────────┘ └───────────┘主要法规覆盖范围
法规 | 适用区域 | 核心要求 | 处罚力度 |
|---|---|---|---|
GDPR | 欧盟/EEA | 数据主体权利、同意机制、数据最小化、DPIA | 全球年营收4%或2000万欧元 |
个保法(PIPL) | 中国 | 知情同意、最小必要、跨境传输评估、个人信息保护影响评估 | 5000万元或上年营业额5% |
CCPA/CPRA | 加利福尼亚 | 数据披露权、删除权、选择退出权 | 每次违规2500-7500美元 |
二、核心代码实现
package main import ( "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/sha256" "encoding/base64" "encoding/json" "fmt" "io" "os" "strings" "sync" "time" ) // ============================================================ // 1. 数据分类分级 // ============================================================ type SensitivityLevel int const ( Low SensitivityLevel = iota // L1 Medium // L2 High // L3 Critical // L4 ) type DataField struct { Name string Sensitivity SensitivityLevel Retention time.Duration Encrypted bool Masked bool ConsentNeed bool } var DefaultFields = map[string]DataField{ "user_id": {"user_id", Medium, 365 * 24 * time.Hour, true, false, false}, "email": {"email", High, 365 * 24 * time.Hour, true, true, true}, "phone": {"phone", High, 180 * 24 * time.Hour, true, true, true}, "id_card": {"id_card", Critical, 90 * 24 * time.Hour, true, true, true}, "address": {"address", High, 180 * 24 * time.Hour, true, true, true}, "credit_card": {"credit_card", Critical, 30 * 24 * time.Hour, true, true, true}, "conversation": {"conversation", Medium, 30 * 24 * time.Hour, true, false, true}, "ip_address": {"ip_address", Low, 7 * 24 * time.Hour, false, false, false}, } // ============================================================ // 2. 数据脱敏 // ============================================================ func MaskField(fieldType, value string) string { runes := []rune(value) switch fieldType { case "email": parts := strings.Split(value, "@") if len(parts) == 2 && len(parts[0]) > 2 { return parts[0][:2] + "***@" + parts[1] } case "phone": if len(runes) == 11 { return string(runes[:3]) + "****" + string(runes[7:]) } case "id_card": if len(runes) == 18 { return string(runes[:6]) + "********" + string(runes[14:]) } case "credit_card": if len(runes) >= 8 { return string(runes[:4]) + "********" + string(runes[len(runes)-4:]) } } if len(runes) <= 2 { return strings.Repeat("*", len(runes)) } return string(runes[0]) + strings.Repeat("*", len(runes)-2) + string(runes[len(runes)-1]) } // ============================================================ // 3. 加密服务 // ============================================================ type Crypto struct { key []byte } func NewCrypto(secret string) *Crypto { h := sha256.Sum256([]byte(secret)) return &Crypto{key: h[:]} } func (c *Crypto) Encrypt(plain string) (string, error) { block, _ := aes.NewCipher(c.key) gcm, _ := cipher.NewGCM(block) nonce := make([]byte, gcm.NonceSize()) io.ReadFull(rand.Reader, nonce) return base64.StdEncoding.EncodeToString(gcm.Seal(nonce, nonce, []byte(plain), nil)), nil } func (c *Crypto) Decrypt(cipherText string) (string, error) { data, _ := base64.StdEncoding.DecodeString(cipherText) block, _ := aes.NewCipher(c.key) gcm, _ := cipher.NewGCM(block) nonceSize := gcm.NonceSize() plain, err := gcm.Open(nil, data[:nonceSize], data[nonceSize:], nil) return string(plain), err } // ============================================================ // 4. 同意管理 // ============================================================ type Purpose struct { ID string Name string LegalBasis string // consent, contract, legal_obligation Required bool Retention time.Duration } var DefaultPurposes = []Purpose{ {"service", "提供服务", "contract", true, 30 * 24 * time.Hour}, {"personalize", "个性化体验", "consent", false, 90 * 24 * time.Hour}, {"training", "模型训练", "consent", false, 365 * 24 * time.Hour}, {"compliance", "合规要求", "legal_obligation", true, 1095 * 24 * time.Hour}, } type ConsentRecord struct { UserID string Purposes map[string]bool // purposeID -> granted GrantedAt map[string]time.Time History []ConsentChange } type ConsentChange struct { PurposeID string OldStatus bool NewStatus bool Time time.Time Channel string } type ConsentManager struct { records map[string]*ConsentRecord mu sync.RWMutex } func NewConsentManager() *ConsentManager { return &ConsentManager{records: make(map[string]*ConsentRecord)} } func (cm *ConsentManager) GetOrCreate(userID string) *ConsentRecord { cm.mu.Lock() defer cm.mu.Unlock() if r, ok := cm.records[userID]; ok { return r } r := &ConsentRecord{ UserID: userID, Purposes: make(map[string]bool), GrantedAt: make(map[string]time.Time), } for _, p := range DefaultPurposes { r.Purposes[p.ID] = p.Required // 必需目的默认同意 if p.Required { r.GrantedAt[p.ID] = time.Now() } } cm.records[userID] = r return r } func (cm *ConsentManager) Grant(userID, purposeID, channel string) error { cm.mu.Lock() defer cm.mu.Unlock() r := cm.records[userID] if r == nil { return fmt.Errorf("用户 %s 无记录", userID) } old := r.Purposes[purposeID] r.Purposes[purposeID] = true r.GrantedAt[purposeID] = time.Now() r.History = append(r.History, ConsentChange{purposeID, old, true, time.Now(), channel}) return nil } func (cm *ConsentManager) Withdraw(userID, purposeID, channel string) error { cm.mu.Lock() defer cm.mu.Unlock() r := cm.records[userID] if r == nil { return fmt.Errorf("用户 %s 无记录", userID) } for _, p := range DefaultPurposes { if p.ID == purposeID && p.Required { return fmt.Errorf("目的 %s 不可撤回", purposeID) } } old := r.Purposes[purposeID] r.Purposes[purposeID] = false delete(r.GrantedAt, purposeID) r.History = append(r.History, ConsentChange{purposeID, old, false, time.Now(), channel}) return nil } func (cm *ConsentManager) Check(userID, purposeID string) bool { cm.mu.RLock() defer cm.mu.RUnlock() r := cm.records[userID] return r != nil && r.Purposes[purposeID] } // ============================================================ // 5. 审计日志 // ============================================================ type AuditEntry struct { ID string `json:"id"` Time time.Time `json:"time"` ActorID string `json:"actor_id"` ActorType string `json:"actor_type"` // user, system, admin Action string `json:"action"` // read, write, delete, export Resource string `json:"resource"` ResourceID string `json:"resource_id"` Outcome string `json:"outcome"` // success, failure, denied Detail map[string]interface{} `json:"detail"` IP string `json:"ip"` Tags []string `json:"tags"` } type AuditLogger struct { store func(AuditEntry) error mu sync.Mutex } func NewAuditLogger(path string) *AuditLogger { os.MkdirAll(path, 0755) return &AuditLogger{ store: func(e AuditEntry) error { date := e.Time.Format("2006-01-02") fname := fmt.Sprintf("%s/audit_%s.jsonl", path, date) data, _ := json.Marshal(e) f, err := os.OpenFile(fname, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return err } defer f.Close() _, err = f.Write(append(data, '\n')) return err }, } } func (al *AuditLogger) Log(entry AuditEntry) error { al.mu.Lock() defer al.mu.Unlock() b := make([]byte, 8) rand.Read(b) entry.ID = fmt.Sprintf("AUD-%x", b) entry.Time = time.Now() if entry.Tags == nil { entry.Tags = inferTags(entry) } return al.store(entry) } func inferTags(e AuditEntry) []string { tags := []string{} if strings.Contains(e.Resource, "email") || strings.Contains(e.Resource, "phone") { tags = append(tags, "gdpr", "pipl") } if e.Action == "export" { tags = append(tags, "ccpa") } return tags } // ============================================================ // 6. 数据主体请求 // ============================================================ type DSRHandler struct { requests map[string]*DSR logger *AuditLogger mu sync.Mutex } type DSR struct { ID string UserID string Type string // access, erase, rectify, port Status string // pending, done, rejected ReceivedAt time.Time Deadline time.Time CompletedAt *time.Time } func NewDSRHandler(logger *AuditLogger) *DSRHandler { return &DSRHandler{ requests: make(map[string]*DSR), logger: logger, } } func (h *DSRHandler) Submit(userID, reqType string) *DSR { h.mu.Lock() defer h.mu.Unlock() dsr := &DSR{ ID: fmt.Sprintf("DSR-%x", time.Now().UnixNano()), UserID: userID, Type: reqType, Status: "pending", ReceivedAt: time.Now(), Deadline: time.Now().Add(30 * 24 * time.Hour), } h.requests[dsr.ID] = dsr h.logger.Log(AuditEntry{ActorID: userID, ActorType: "user", Action: "submit_dsr", Resource: "personal_data", Outcome: "success"}) return dsr } func (h *DSRHandler) Complete(id string) error { h.mu.Lock() defer h.mu.Unlock() r, ok := h.requests[id] if !ok { return fmt.Errorf("请求 %s 不存在", id) } r.Status = "done" now := time.Now() r.CompletedAt = &now return nil } // ============================================================ // 7. 合规检查清单 // ============================================================ type ComplianceCheck struct { ID string Name string Pass bool Detail string } type ComplianceReport struct { System string Checks []ComplianceCheck PassRate float64 Generated time.Time } func RunComplianceCheck(system string, config map[string]interface{}) ComplianceReport { checks := []ComplianceCheck{ {"GDPR-5-1C", "数据最小化", false, ""}, {"GDPR-5-1E", "存储期限限制", false, ""}, {"GDPR-7", "同意机制", false, ""}, {"GDPR-17", "删除权支持", false, ""}, {"GDPR-20", "数据可携性", false, ""}, {"PIPL-13", "告知同意", false, ""}, {"PIPL-38", "跨境传输评估", false, ""}, {"CCPA-1798", "选择退出权", false, ""}, } for i, c := range checks { switch c.ID { case "GDPR-5-1C": fields, _ := config["collected_fields"].([]string) checks[i].Pass = len(fields) <= 10 // 简单判断 checks[i].Detail = fmt.Sprintf("收集字段数: %d", len(fields)) case "GDPR-5-1E": retention, _ := config["max_retention_days"].(float64) checks[i].Pass = retention <= 365 checks[i].Detail = fmt.Sprintf("最长保留天数: %.0f", retention) case "GDPR-7": checks[i].Pass = true // 假设已实现同意管理 checks[i].Detail = "同意管理器已部署" case "GDPR-17": checks[i].Pass = true checks[i].Detail = "DSR 处理器已部署" case "PIPL-13": checks[i].Pass = true checks[i].Detail = "隐私政策已更新" case "CCPA-1798": checks[i].Pass = true checks[i].Detail = "选择退出接口已提供" } } passCount := 0 for _, c := range checks { if c.Pass { passCount++ } } return ComplianceReport{ System: system, Checks: checks, PassRate: float64(passCount) / float64(len(checks)) * 100, Generated: time.Now(), } } // ============================================================ // 8. 主程序演示 // ============================================================ func main() { fmt.Println("========== 第9讲:合规与审计 ==========\n") // 1. 数据脱敏演示 fmt.Println("--- 数据脱敏 ---") fmt.Printf("邮箱: %s\n", MaskField("email", "zhangsan@example.com")) fmt.Printf("手机: %s\n", MaskField("phone", "13812345678")) fmt.Printf("身份证: %s\n", MaskField("id_card", "110101199001011234")) // 2. 加密演示 crypto := NewCrypto("my-secret-key-32bytes!") enc, _ := crypto.Encrypt("sensitive-data-123") dec, _ := crypto.Decrypt(enc) fmt.Printf("\n--- 加密 ---\n原文: sensitive-data-123\n密文: %s\n解密: %s\n", enc[:20]+"...", dec) // 3. 同意管理演示 cm := NewConsentManager() cm.GetOrCreate("user_001") cm.Grant("user_001", "personalize", "web") fmt.Printf("\n--- 同意管理 ---\n服务同意: %v\n个性化同意: %v\n", cm.Check("user_001", "service"), cm.Check("user_001", "personalize")) // 4. 审计日志演示 logger := NewAuditLogger("./audit_logs") logger.Log(AuditEntry{ ActorID: "user_001", ActorType: "user", Action: "read", Resource: "conversation", ResourceID: "conv_123", Outcome: "success", IP: "192.168.1.1", }) fmt.Println("\n--- 审计日志 ---") fmt.Println("审计条目已写入 ./audit_logs/") // 5. 合规检查 config := map[string]interface{}{ "collected_fields": []string{"user_id", "email", "phone", "conversation"}, "max_retention_days": 180.0, } report := RunComplianceCheck("AI Chat System", config) fmt.Printf("\n--- 合规检查报告 ---\n系统: %s\n通过率: %.0f%%\n检查项:\n", report.System, report.PassRate) for _, c := range report.Checks { status := "❌" if c.Pass { status = "✅" } fmt.Printf(" %s [%s] %s - %s\n", status, c.ID, c.Name, c.Detail) } // 6. 数据主体请求 dsrHandler := NewDSRHandler(logger) req := dsrHandler.Submit("user_001", "access") dsrHandler.Complete(req.ID) fmt.Printf("\n--- 数据主体请求 ---\n请求ID: %s\n类型: %s\n状态: %s\n截止日: %s\n", req.ID, req.Type, req.Status, req.Deadline.Format("2006-01-02")) }三、合规检查清单
数据保护合规
检查项 | 法规依据 | 实现要求 | 验证方式 |
|---|---|---|---|
数据分类分级 | GDPR Art.5 | 建立数据清单,标记敏感度 | 自动化扫描 |
最小化收集 | GDPR Art.5(1)(c) | 仅收集必需字段 | 字段审查 |
存储期限限制 | GDPR Art.5(1)(e) | 设定保留策略,到期自动清理 | 定时任务 |
同意管理 | GDPR Art.7 | 获取、记录、撤回机制 | 审计日志 |
数据脱敏 | PIPL 第6条 | 展示/导出时脱敏 | 功能测试 |
加密存储 | GDPR Art.32 | AES-256 加密 | 密钥管理审计 |
删除权 | GDPR Art.17 | 用户可申请删除数据 | DSR 流程测试 |
数据可携性 | GDPR Art.20 | 导出标准化格式 | 导出功能测试 |
模型治理合规
检查项 | 说明 | 实施建议 |
|---|---|---|
公平性审计 | 检测模型偏见 | 定期偏差测试 |
可解释性 | 决策理由可追溯 | SHAP/LIME 集成 |
透明度 | 告知用户与AI交互 | 对话标识 |
人工干预 | 用户可转接人工 | 升级机制 |
四、合规架构图
用户请求 │ ▼ ┌──────────────┐ ┌──────────────┐ │ 同意检查 │◄────│ 同意管理器 │ │ (ConsentCheck)│ │ (ConsentDB) │ └──────┬───────┘ └──────────────┘ │ ▼ ┌──────────────┐ ┌──────────────┐ │ 数据分类 │◄────│ 数据清单 │ │ (Classify) │ │ (Inventory) │ └──────┬───────┘ └──────────────┘ │ ▼ ┌──────────────┐ │ 脱敏/加密 │ │ (Mask/Encrypt)│ └──────┬───────┘ │ ▼ ┌──────────────┐ ┌──────────────┐ │ 处理/存储 │────►│ 审计日志 │ │ (Process) │ │ (AuditLog) │ └──────┬───────┘ └──────────────┘ │ ▼ ┌──────────────┐ │ 数据生命周期 │ │ (Retention) │ └──────────────┘五、关键要点
- 合规是持续过程 — 不是一次性项目,需定期审查和更新
- 数据清单是基础 — 不知道有什么数据就无法保护
- 同意管理是核心 — 记录每一次同意的授予和撤回
- 审计日志不可篡改 — 采用追加写入,定期归档
- DSR 有时限 — GDPR 要求30天内回应,超时即违规
- 跨境传输需评估 — 标准合同条款(SCC)或充分性认定
🧰 开发之余的小工具推荐
做合规工作时经常需要解析 JWT 看用户会话、格式化 JSON 对比数据差异、Base64 编解码调试加密逻辑。我常用 zz365.top 这个纯前端工具箱,所有计算在浏览器本地完成,文件不上传服务器,关页即清。免费、无登录、无广告,适合日常开发调试。
下一讲:第10讲:总结与最佳实践 — 全系列回顾、安全架构演进路线、生产部署 checklist、团队建设建议、未来趋势展望。