gdpr-compliant

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

GDPR Engineering Skill

GDPR工程技能

Actionable GDPR reference for engineers, architects, DevOps, and tech leads. Inspired by CNIL developer guidance and GDPR Articles 5, 25, 32, 33, 35.
Golden Rule: Collect less. Store less. Expose less. Retain less.
For deep dives, read the reference files in
references/
:
  • references/data-rights.md
    — user rights endpoints, DSR workflow, RoPA
  • references/security.md
    — encryption, hashing, secrets, anonymization
  • references/operations.md
    — cloud, CI/CD, incident response, architecture patterns

面向工程师、架构师、DevOps和技术负责人的可落地GDPR参考指南,灵感来自CNIL开发者指南和GDPR第5、25、32、33、35条。
黄金法则: 少收集、少存储、少暴露、少留存。
如需深入了解,请阅读
references/
目录下的参考文件:
  • references/data-rights.md
    — 用户权利接口、数据主体请求(DSR)工作流、处理活动记录(RoPA)
  • references/security.md
    — 加密、哈希、密钥、匿名化
  • references/operations.md
    — 云服务、CI/CD、事件响应、架构模式

1. Core GDPR Principles (Article 5)

1. GDPR核心原则(第5条)

PrincipleEngineering obligation
Lawfulness, fairness, transparencyDocument legal basis for every processing activity in the RoPA
Purpose limitationData collected for purpose A MUST NOT be reused for purpose B without a new legal basis
Data minimizationCollect only fields with a documented business need today
AccuracyProvide update endpoints; propagate corrections to downstream stores
Storage limitationDefine TTL at schema design time — never after
Integrity & confidentialityEncrypt at rest and in transit; restrict and audit access
AccountabilityMaintain evidence of compliance; RoPA ready for DPA inspection at any time

原则工程层面责任
合法性、公平性、透明度在RoPA中记录每项数据处理活动的法律依据
目的限制为A目的收集的数据禁止在没有新法律依据的情况下被用于B目的
数据最小化仅收集当前有明确业务需求的字段
准确性提供数据更新接口;将数据更正同步到所有下游存储
存储限制在设计表结构时就定义TTL(生存时间)——绝对不要事后补
完整性与保密性静态和传输中的数据都要加密;限制数据访问权限并留存审计日志
问责制留存合规证明;RoPA随时可接受数据保护机构(DPA)检查

2. Privacy by Design & by Default

2. 设计即隐私、默认即隐私

MUST
  • Add
    CreatedAt
    ,
    RetentionExpiresAt
    to every table holding personal data at creation time.
  • Default all optional data collection to off. Users opt in; they never opt out of a default-on setting.
  • Conduct a DPIA before building high-risk processing (biometrics, health data, large-scale profiling, systematic monitoring).
  • Update the RoPA with every new feature that introduces a processing activity.
  • Sign a DPA with every sub-processor before data flows to them.
MUST NOT
  • Ship a new data collection feature without a documented legal basis.
  • Enable analytics, tracking, or telemetry by default without explicit consent.
  • Store personal data in a system not listed in the RoPA.

必须做到
  • 所有存储个人数据的表在创建时都要添加
    CreatedAt
    RetentionExpiresAt
    字段。
  • 所有可选数据收集项默认关闭。用户必须主动选择开启,不允许设置默认开启需要用户手动关闭的选项。
  • 在构建高风险数据处理能力(生物识别、健康数据、大规模用户画像、系统性监控)前必须开展数据保护影响评估(DPIA)
  • 每次新增涉及数据处理活动的功能时都要更新RoPA
  • 在数据流向任何子处理方之前,必须和对方签署数据处理协议(DPA)
禁止
  • 上线没有明确法律依据的新数据收集功能。
  • 没有获得用户明确同意的情况下默认开启分析、跟踪或遥测功能。
  • 将个人数据存储在未列入RoPA的系统中。

3. Data Minimization

3. 数据最小化

MUST
  • Map every DTO/model field to a concrete business need. Remove undocumented fields.
  • Use separate DTOs for create, read, and update — never reuse the same object.
  • Return only what the caller is authorized to see — use response projections.
  • Mask sensitive values at the edge: return
    ****1234
    for card numbers, never the full value.
  • Exclude sensitive fields (DOB, national ID, health) from default list/search projections.
MUST NOT
  • Log full request/response bodies if they may contain personal data.
  • Include personal data in URL path segments or query parameters (CDN logs, browser history).
  • Collect
    dateOfBirth
    , national ID, or health data without an explicit legal basis.

必须做到
  • 每个DTO/模型字段都要对应明确的业务需求,删除没有记录用途的字段。
  • 新增、查询、更新操作使用独立的DTO——绝对不要复用同一个对象。
  • 仅返回调用方有权限查看的数据——使用响应投影。
  • 在边缘层对敏感值做掩码处理:卡号仅返回
    ****1234
    ,绝对不要返回完整值。
  • 默认列表/搜索投影排除敏感字段(出生日期、身份证号、健康数据)。
禁止
  • 如果请求/响应体可能包含个人数据,不要记录完整的请求/响应体日志。
  • 不要将个人数据放在URL路径段或查询参数中(会被CDN日志、浏览器历史记录留存)。
  • 没有明确法律依据的情况下不要收集出生日期、身份证号或健康数据。

4. Purpose Limitation

4. 目的限制

MUST
  • Document the purpose of every processing activity in code comments and in the RoPA.
  • Obtain a new legal basis or perform a compatibility analysis before reusing data for a secondary purpose.
MUST NOT
  • Share personal data collected for service delivery with advertising networks without explicit consent.
  • Use support ticket content to train ML models without a separate legal basis and user notice.

必须做到
  • 在代码注释和RoPA中记录每项数据处理活动的目的。
  • 将数据用于二次目的前,必须获得新的法律依据或完成兼容性分析。
禁止
  • 没有获得用户明确同意的情况下,将为服务交付收集的个人数据共享给广告网络。
  • 没有单独的法律依据和用户告知的情况下,使用工单内容训练ML模型。

5. Storage Limitation & Retention

5. 存储限制与数据留存

MUST
  • Every table holding personal data MUST have a defined retention period.
  • Enforce retention automatically via a scheduled job (Hangfire, cron) — never a manual process.
  • Anonymize or delete data when retention expires — never leave expired data silently in production.
Recommended defaults
Data typeMax retention
Auth / audit logs12–24 months
Session / refresh tokens30–90 days
Email / notification logs6 months
Inactive user accounts12 months after last login → notify → delete
Payment recordsAs required by tax law (7–10 years), minimized
Analytics events13 months
SHOULD
  • Add
    RetentionExpiresAt
    column — compute at insert time.
  • Use soft-delete (
    DeletedAt
    ) with a scheduled hard-delete after the erasure request window (30 days).
MUST NOT
  • Retain personal data indefinitely "in case it becomes useful later."

必须做到
  • 所有存储个人数据的表必须定义明确的留存周期。
  • 通过定时任务(Hangfire、cron)自动执行留存规则——绝对不要使用人工流程。
  • 留存期到期后对数据做匿名化或删除处理——绝对不要让过期数据静默留在生产环境中。
推荐默认留存周期
数据类型最长留存期
身份验证/审计日志12–24个月
会话/刷新令牌30–90天
邮件/通知日志6个月
非活跃用户账户最后一次登录后12个月 → 通知用户 → 删除
支付记录按税法要求留存(7–10年),仅保留最小必要字段
分析事件13个月
建议做到
  • 添加
    RetentionExpiresAt
    字段——在数据插入时就计算好到期时间。
  • 使用软删除(
    DeletedAt
    ),在删除请求窗口期(30天)结束后通过定时任务执行硬删除。
禁止
  • 「以后可能有用」为由无限期留存个人数据。

6. API Design Rules

6. API设计规则

MUST
  • MUST NOT include personal data in URL paths or query parameters.
    • GET /users/{userId}
  • Authenticate all endpoints that return or accept personal data.
  • Extract the acting user's identity from the JWT — never from the request body.
  • Validate ownership on every resource:
    if (resource.OwnerId != currentUserId) return 403
    .
  • Use UUIDs or opaque identifiers — never sequential integers as public resource IDs.
SHOULD
  • Rate-limit sensitive endpoints (login, data export, password reset).
  • Set
    Referrer-Policy: no-referrer
    and an explicit
    CORS
    allowlist.
MUST NOT
  • Return stack traces, internal paths, or database errors in API responses.
  • Use
    Access-Control-Allow-Origin: *
    on authenticated APIs.

必须做到
  • 禁止将个人数据放在URL路径或查询参数中。
    • 反例:
      GET /users/{userId}
  • 所有返回或接收个人数据的接口都要做身份验证。
  • 从JWT中提取操作人的身份信息——绝对不要从请求体中获取。
  • 对所有资源做所有权校验:
    if (resource.OwnerId != currentUserId) return 403
  • 使用UUID或不透明标识符——绝对不要使用自增整数作为公开资源ID。
建议做到
  • 对敏感接口(登录、数据导出、密码重置)做限流。
  • 设置
    Referrer-Policy: no-referrer
    和明确的CORS白名单。
禁止
  • 在API响应中返回栈轨迹、内部路径或数据库错误。
  • 对需要身份验证的API设置
    Access-Control-Allow-Origin: *

7. Logging Rules

7. 日志规则

MUST
  • Anonymize IPs in application logs — mask last octet (IPv4) or last 80 bits (IPv6).
    • 192.168.1.xxx
  • MUST NOT log: passwords, tokens, session IDs, credentials, card numbers, national IDs, health data.
  • MUST NOT log full request/response bodies where PII may be present.
  • Enforce log retention — purge automatically after the defined period.
SHOULD
  • Log events not data:
    "User {UserId} updated email"
    not
    "Email changed from a@b.com to c@d.com"
    .
  • Use structured logging (JSON) with
    userId
    as an internal identifier, not the email address.
  • Separate audit logs (sensitive access, admin actions) from application logs — different retention and ACLs.

必须做到
  • 应用日志中的IP要做匿名化处理:掩盖IPv4的最后一段、IPv6的最后80位。
    • 示例:
      192.168.1.xxx
  • 禁止记录:密码、令牌、会话ID、凭证、卡号、身份证号、健康数据。
  • 如果请求/响应体可能包含个人可识别信息(PII),禁止记录完整的请求/响应体。
  • 执行日志留存规则——到期后自动清理。
建议做到
  • 记录事件而非具体数据:记录
    "用户 {UserId} 更新了邮箱"
    ,不要记录
    "邮箱从a@b.com修改为c@d.com"
  • 使用结构化日志(JSON),用
    userId
    作为内部标识符,不要使用邮箱地址。
  • 将审计日志(敏感访问、管理员操作)和应用日志分开存储——设置不同的留存周期和访问权限。

8. Error Handling

8. 错误处理

MUST
  • Return generic error messages — never expose stack traces, internal paths, or DB errors.
    • "Column 'email' violates unique constraint on table 'users'"
    • "A user with this email address already exists."
  • Use Problem Details (RFC 7807) for all error responses.
  • Log the full error server-side with a correlation ID; return only the correlation ID to the client.
MUST NOT
  • Include file paths, class names, or line numbers in error responses.
  • Include personal data in error messages (e.g., "User john@example.com not found").

必须做到
  • 返回通用错误信息——绝对不要暴露栈轨迹、内部路径或数据库错误。
    • 反例:
      "Column 'email' violates unique constraint on table 'users'"
    • 正例:
      "该邮箱地址已被注册。"
  • 所有错误响应使用问题详情格式(RFC 7807)
  • 服务端记录带关联ID的完整错误信息,仅将关联ID返回给客户端。
禁止
  • 在错误响应中包含文件路径、类名或代码行号。
  • 在错误信息中包含个人数据(比如「用户john@example.com不存在」)。

9. Encryption (summary — see
references/security.md
for full detail)

9. 加密(概述——完整内容见
references/security.md

ScopeMinimum standard
Standard personal dataAES-256 disk/volume encryption
Sensitive data (health, financial, biometric)AES-256 column-level + envelope encryption via KMS
In transitTLS 1.2+ (prefer 1.3); HSTS enforced
KeysHSM-backed KMS; rotate DEKs annually
MUST NOT allow TLS 1.0/1.1, null cipher suites, or hardcoded encryption keys.

范围最低标准
普通个人数据AES-256磁盘/卷加密
敏感数据(健康、金融、生物识别)AES-256列级加密 + 通过KMS实现信封加密
传输中数据TLS 1.2+(优先使用1.3);强制开启HSTS
密钥基于HSM的KMS;每年轮换数据加密密钥(DEK)
禁止使用TLS 1.0/1.1、空加密套件,或者硬编码加密密钥。

10. Password Hashing

10. 密码哈希

MUST
  • Use Argon2id (recommended) or bcrypt (cost ≥ 12). Never MD5, SHA-1, or SHA-256.
  • Use a unique salt per password. Store only the hash.
MUST NOT
  • Log passwords in any form. Transmit passwords in URLs. Store reset tokens in plaintext.

必须做到
  • 使用Argon2id(推荐)或bcrypt(成本因子≥12)。绝对不要使用MD5、SHA-1或SHA-256。
  • 每个密码使用唯一的盐值,仅存储哈希结果。
禁止
  • 以任何形式记录密码、通过URL传输密码、明文存储重置令牌。

11. Secrets Management

11. 密钥管理

MUST
  • Store all secrets in a KMS: Azure Key Vault, AWS Secrets Manager, GCP Secret Manager, or HashiCorp Vault.
  • Use pre-commit hooks (
    gitleaks
    ,
    detect-secrets
    ) to prevent secret commits.
  • Rotate secrets on developer offboarding, annual schedule, or suspected compromise.
.gitignore
MUST include:
.env
,
.env.*
,
*.pem
,
*.key
,
*.pfx
,
*.p12
,
secrets/
MUST NOT
  • Commit secrets to source code. Store secrets as plain-text environment variable defaults.

必须做到
  • 所有密钥存储在KMS中:Azure Key Vault、AWS Secrets Manager、GCP Secret Manager或HashiCorp Vault。
  • 使用pre-commit钩子(
    gitleaks
    detect-secrets
    )防止密钥被提交到代码库。
  • 在开发者离职、年度轮换周期、或疑似泄露时轮换密钥。
.gitignore
必须包含:
.env
.env.*
*.pem
*.key
*.pfx
*.p12
secrets/
禁止
  • 将密钥提交到源代码库、将密钥作为明文环境变量默认值存储。

12. Anonymization & Pseudonymization (summary — see
references/security.md
)

12. 匿名化与假名化(概述——完整内容见
references/security.md

  • Anonymization = irreversible → falls outside GDPR scope. Use for retained records after erasure.
  • Pseudonymization = reversible with a key → still personal data, reduced risk.
  • When erasing a user, anonymize records that must be retained (financial, audit) rather than deleting them.
  • Store the pseudonymization key in the KMS — never in the same database as the pseudonymized data.
MUST NOT call data "anonymized" if re-identification is possible through linkage attacks.

  • 匿名化 = 不可逆处理 → 不在GDPR监管范围内。适用于删除用户后需要留存的记录。
  • 假名化 = 持有密钥可恢复 → 仍属于个人数据,风险等级更低。
  • 擦除用户数据时,对需要留存的记录(金融、审计)做匿名化处理而非直接删除。
  • 假名化密钥存储在KMS中——绝对不要和假名化数据存在同一个数据库。
禁止将可通过关联攻击重新识别的数据称为「匿名化」数据。

13. Testing with Fake Data

13. 测试数据规范

MUST
  • MUST NOT use production personal data in dev, staging, or CI environments.
  • MUST NOT restore production DB backups to non-production without scrubbing PII first.
  • Use synthetic data generators:
    Bogus
    (.NET),
    Faker
    (JS/Python/Ruby).
  • Use
    @example.com
    for all test email addresses.

必须做到
  • 禁止在开发、测试、CI环境中使用生产环境的个人数据。
  • 禁止在没有 scrub 掉PII的情况下将生产数据库备份恢复到非生产环境。
  • 使用合成数据生成工具:
    Bogus
    (.NET)、
    Faker
    (JS/Python/Ruby)。
  • 所有测试邮箱地址使用
    @example.com
    后缀。

14. Anti-Patterns

14. 反模式

Anti-patternCorrect approach
PII in URLsOpaque UUIDs as public identifiers
Logging full request bodiesLog structured event metadata only
"Keep forever" schemaTTL defined at design time
Production data in dev/testSynthetic data + scrubbing pipeline
Shared credentials across teamsIndividual accounts + RBAC
Hardcoded secretsKMS + secret manager
Access-Control-Allow-Origin: *
on auth APIs
Explicit CORS allowlist
Storing consent with profile dataDedicated consent store
PII in GET query paramsPOST body or authenticated session
Sequential integer IDs in public URLsUUIDs
"Anonymized" data with quasi-identifiersApply k-anonymity, test linkage resistance
Mixing backup regions outside EEAExplicit region lockdown on backup jobs

反模式正确做法
URL中包含PII使用不透明UUID作为公开标识符
记录完整请求体日志仅记录结构化事件元数据
「永久留存」的表结构设计时就定义TTL
开发/测试环境使用生产数据合成数据 + 数据清洗流水线
团队共享凭证独立账号 + RBAC
硬编码密钥KMS + 密钥管理器
身份验证API设置
Access-Control-Allow-Origin: *
明确的CORS白名单
用户同意状态和个人资料存在一起独立的同意状态存储
GET查询参数中包含PII放在POST请求体或已验证的会话中
公开URL中使用自增整数ID使用UUID
包含准标识符的「匿名化」数据应用k-匿名性,测试抗关联能力
备份存储区域超出欧洲经济区(EEA)备份任务明确限制存储区域

15. PR Review Checklist

15. PR评审检查清单

Data model

数据模型

  • Every new PII column has a documented purpose and retention period.
  • Sensitive fields (health, financial, national ID) use column-level encryption.
  • No sequential integer PKs as public-facing identifiers.
  • 所有新增的PII字段都有明确的用途记录和留存周期。
  • 敏感字段(健康、金融、身份证号)使用列级加密。
  • 没有将自增整数主键作为对外暴露的标识符。

API

API

  • No PII in URL paths or query parameters.
  • All endpoints returning personal data are authenticated.
  • Ownership checks present — user cannot access another user's resource.
  • Rate limiting applied to sensitive endpoints.
  • URL路径和查询参数中没有PII。
  • 所有返回个人数据的接口都做了身份验证。
  • 存在所有权校验——用户无法访问其他用户的资源。
  • 敏感接口配置了限流。

Logging

日志

  • No passwords, tokens, or credentials logged.
  • IPs anonymized (last octet masked).
  • No full request/response bodies logged where PII may be present.
  • 没有记录密码、令牌或凭证。
  • IP做了匿名化处理(最后一段掩码)。
  • 可能包含PII的场景没有记录完整的请求/响应体。

Infrastructure

基础设施

  • No public storage buckets or public-IP databases.
  • New cloud resources tagged with
    DataClassification
    .
  • Encryption at rest enabled for new storage resources.
  • New geographic regions for data storage are EEA-compliant or covered by SCCs.
  • 没有公开的存储桶或公网可访问的数据库。
  • 新的云资源都打了
    DataClassification
    标签。
  • 新的存储资源开启了静态加密。
  • 新增的数据存储地理区域符合EEA要求,或已签署标准合同条款(SCCs)。

Secrets & CI/CD

密钥与CI/CD

  • No secrets in source code or committed config files.
  • New secrets added to KMS and secrets inventory document.
  • CI/CD secrets masked in pipeline logs.
  • 源代码或提交的配置文件中没有密钥。
  • 新密钥已添加到KMS和密钥清单文档中。
  • CI/CD流水线日志中的密钥已做掩码处理。

Retention & erasure

留存与擦除

  • Retention enforcement job or policy covers new data store or field.
  • Erasure pipeline updated to cover new data store.
  • 新增的数据存储或字段已被纳入留存规则执行任务或策略覆盖范围。
  • 数据擦除流水线已更新覆盖新增的数据存储。

User rights & governance

用户权利与治理

  • Data export endpoint includes any new personal data field.
  • RoPA updated if a new processing activity is introduced.
  • New sub-processors have a signed DPA and a RoPA entry.
  • DPIA triggered if the change involves high-risk processing.

Golden Rule: Collect less. Store less. Expose less. Retain less.
Every byte of personal data you do not collect is a byte you cannot lose, cannot breach, and cannot be held liable for.

Inspired by CNIL developer GDPR guidance, GDPR Articles 5, 25, 32, 33, 35, ENISA, OWASP, and NIST engineering best practices.
  • 数据导出接口包含所有新增的个人数据字段。
  • 如果新增了数据处理活动,已更新RoPA。
  • 新的子处理方已签署DPA并在RoPA中有记录。
  • 如果变更涉及高风险数据处理,已触发DPIA。

黄金法则: 少收集、少存储、少暴露、少留存。
你没有收集的每一字节个人数据,都是你不会丢失、不会泄露、不需要为之承担责任的数据。

灵感来自CNIL开发者GDPR指南、GDPR第5、25、32、33、35条,ENISA、OWASP和NIST工程最佳实践。