
简介《C0131-2018国家政务服务平台统一身份认证隐私保护要求》是一份面向政务服务平台建设者、身份认证系统研发与隐私合规人员的规范文档可用于统一身份认证场景下的数据定级、判定、使用边界及责任划分参考。资源包仅含1个PDF文件大小约188KB正文按范围、规范性引用文件、术语和定义、隐私数据定级与判定、数据收集保存使用、共享披露、导出输出、归档销毁及责任管理等章节组织并附自然人身份信息隐私判定说明。已有818人学习适合需要梳理三级隐私数据保护要求、落实最小必要与知情同意原则、建立检查检测机制的读者查阅也可作为政务身份认证项目合规自查与方案设计的案头资料。1. 三个等级为什么“把敏感字段全加密”反而是最脆弱的做法聊政务服务平台统一身份认证隐私保护最容易踩的认知误区是以为隐私保护就等于“给敏感字段加密”。可这份编号 C 0131-2018 的规范给出的定级结果相当反直觉证件类型、性别、民族、国籍、实名等级这些听起来很“个人”的字段被压到一级最低档不作特殊存储要求姓名和手机号只是二级真正被要求“互联网区只能存不可逆散列”的是证件编号、户籍地址这类三级数据。分档依据不是字段读起来敏不敏感而是泄露之后能不能回指到某个具体的人。搞懂这条主线后面所有的脱敏阈值、密钥托管、临时明文时间窗才不是拍脑袋的数字。2. 隐私数据定级可识别性怎么决定一个字段落在哪一档定级是整个规范的地基。它一旦定错后面的存储方式、脱敏规则、导出审批全部跟着错而且是那种“系统跑得好好的、没人报警”的错。所以这一章先把分档逻辑拆开看再谈字段。规范给出的三句话定义非常短一级数据是“不能对应到自然人实体身份信息的数据”二级是“可用于标识自然人实体的身份信息且较公开”三级是“可用于标识自然人实体的身份信息且具有较高隐私性”。注意这三个定义的动词都是“对应”“标识”全部指向同一件事——这条数据能不能把记录连回一个活人。敏感度只是第二维的修正项不是主判据。2.1 一级数据不是“不敏感”而是“单独拿出来回指不到人”把附录判定表里的一级字段列出来看证件类型、证件散列码、实名等级、实名核验日期、性别、用户类型、民族、国籍。清一色的特征是——信息量太低单独使用时不构成识别。比如“证件类型居民身份证”这条记录全国十几亿人都成立“性别女”就更不用说。它们真正的价值是作为筛选维度存在。这里就出现一个容易被忽略的漏洞一级数据的安全性是有条件的。性别、生日二级、户籍地址三级这几项如果同时出现在一张结果集里重识别re-identification的门槛会低到令人不安——小样本群体里几个准标识符的组合往往就能锁定唯一记录。规范显然意识到了这一点但它的处理方式不是把字段往上提级而是在第 6.5 条直接禁止“以两个或两个以上用户的个人信息进行关联分析”。这是一个双保险结构定级表负责单条记录的泄露后果关联禁令负责组合后的泄露后果。只做前者不做后者等于给每个字段都上了锁却把钥匙串挂在门上。顺带说一句“未满 14 岁的儿童可提升数据隐私级别”。这条不是可选项它意味着定级是运行时的判定而不是建表时的静态标注。常见的实现思路是在实名核验返回年龄之后、写库之前完成提级而不是在展示层补一层判断——因为一旦明文已经落到磁盘上后面再怎么提级都晚了。2.2 判定表之外还有四类数据要单独处理附录 A 那张表是字段级的穷举白名单但规范在正文第 5 章额外点了四类“表外数据”这四类恰恰是实操中最容易漏的数据级别判定理由人脸、声纹二级无配合也能被采集天然“较公开”指纹三级强绑定、终身不可更换且本身即认证凭据登录记录、门户访问记录二级行为轨迹可推断身份与偏好未满 14 岁儿童的相关信息可提级主体无完全民事行为能力风险敞口更大指纹比人脸高一级这个安排很多人第一反应是“人脸不是更能定位到人吗”。判定的关键不在“能不能定位”而在“泄露后能不能止损”。人脸可以换角度、可以戴口罩声纹会随年龄变化而指纹一旦以图像或模板形式泄露这个人一辈子就那几个指纹换不了更麻烦的是指纹常常直接充当认证因子泄露等价于密码泄露且不可重置。这条取舍在生物特征库里是硬约束做多因子认证的产品线应该把它写进字段策略表而不是靠开发者记忆。2.3 用户标识为什么是散列值可匹配但不可逆推的连接器规范对“用户标识”的定义很精确用户个人信息中的证件号码等唯一识别码形成的散列值在统一身份认证系统中唯一。这句话里藏着一个精妙的设计——跨节点要“对上人”就必须有一个全平台一致的键但跨节点传输时又不能把证件号明文甩来甩去。散列值刚好满足这两个要求同一输入得到同一输出可做等值匹配输出不可逆推拿到散列也还原不出证件号。代价也随之而来。正因为它全平台唯一、跨节点一致它同时成了一个万能连接键任何两个都存了该散列的系统都能在不接触明文的前提下完成 join。所以规范在禁止关联分析时特意补了一句“包括以散列值进行分析”。这句看似多余的话恰恰是整份文档里最有分量的一句限制——它承认了散列化不等于匿名化。也正因为散列函数由国家级节点统一提供地方节点如果自己换算法、自己加盐跨节点查询立刻对不上号。工程上常见的做法是把“用于比对的散列”和“用于本地去标识的散列”分成两套前者严格走统一算法后者按业务域加盐避免所有场景共用一把钥匙。3. 存储与密钥可逆加密和不可逆散列的边界画在哪定级解决“这条数据多重要”存储解决“它该以什么形态落盘”。这两步之间的映射关系是整份规范里最刚性的一段几乎没有解释空间也最容易被“先跑通再说”的临时方案破坏。3.1 同一条三级数据跨一条网络边界就换一种存法规范对存储的要求按区域和级别交叉给出摊开成表是这样的级别互联网区电子政务外网依据一级按通用安全要求按通用安全要求不作特殊要求二级加密存储可逆加密存储可逆SM4 对称加密三级不可逆散列禁止明文或可逆加密加密存储可逆SM2/SM4三级数据在互联网区“不得以明文或可逆加密方式存储”且散列方法必须采用国家级节点统一提供的散列函数。而在电子政务外网里三级数据允许用可逆加密存。同一条数据跨一条网络边界就换一种存法——这个差异不是随手写的它对应的是两个区域暴露面的差异互联网区面对的是任意来源的攻击者一旦库被拖走可逆加密在密钥也失守的情况下等于明文电子政务外网是受控网络接入方有身份、有等级保护约束可逆加密的残余风险被认为可接受。这条规则会立刻引出一个工程矛盾业务要回显“证件号后四位”可库里存的是散列值散列值算不出后四位。规范没有直接给答案常见的落地方式是拆字段——“散列主值”进三级“末四位展示片段”单独作为低级别字段存储展示层只读片段任何需要完整号码的动作都走临时明文通道。这是个明确的取舍用一点额外的可枚举信息换掉整条数据的可逆存储。存储策略如果只写在文档里早晚会被某个新加的字段绕过去。所以更稳的做法是把它写成代码里的硬约束# 字段 - (隐私级别, 互联网区存储方式, 电子政务外网存储方式, 是否允许回显原文) FIELD_POLICY { cert_no: (3, hash, encrypt, False), # 证件编号 household_addr: (3, hash, encrypt, False), # 户籍地址 social_card_no: (3, hash, encrypt, False), # 社保卡号 mobile: (2, encrypt, encrypt, True), # 手机号脱敏回显 real_name: (2, encrypt, encrypt, True), # 姓名脱敏回显 gender: (1, plain, plain, True), # 一级不强制加密 } def storage_plan(field: str, zone: str) - dict: level, inet_mode, extranet_mode, echo_raw FIELD_POLICY[field] mode inet_mode if zone internet else extranet_mode # 硬约束三级数据在互联网区只允许不可逆散列 if level 3 and zone internet: assert mode hash, f{field} 在互联网区不得可逆存储 return {level: level, mode: mode, echo_raw: echo_raw}这段代码的价值不在逻辑复杂而在于把“级别→存储方式”的映射从人的记忆里挪到了断言里。新增字段时如果忘了登记FIELD_POLICY[field]会直接抛KeyError比默默按默认明文存进去安全得多。echo_rawFalse是给序列化层的信号这个字段永远不允许原样出现在响应体里。要扩展的话把 zone 换成枚举、把策略表挪到配置中心都行但那条assert建议保留——它是最后一道后悔药。失败时优先看两处mode是不是被某个“临时兼容分支”改成了encrypt以及echo_raw有没有在 DTO 转换时被忽略。3.2 密钥托管为什么管密钥的人不许碰数据规范对密钥的要求写得很细密钥必须存放在符合密钥管理技术规范的系统或设备中密钥由专人管理密钥管理人员不得从事数据操作、加工、使用等工作密钥管理人员应是本节点正式工作人员不得来自软件开发商、服务商等第三方机构加密使用 SM2、SM4 算法。这几句话合在一起描述的是一个职责分离模型动机非常直接如果同一个人既能拿到密钥、又能查库那加密只是给审计报告看的形式。而“不得来自开发商、服务商”这一条针对的是更现实的一种失效——很多项目的实施方工程师同时持有数据库高权限和密钥配置权限系统上线即等于把明文交出去。把这个威胁写进合规条款等于强制甲方必须在验收时检查“谁同时握着这两把钥匙”。工程上对应的做法是让应用只接触“密文进出”的接口加密在密码设备侧完成应用拿到的是密文和句柄解密必须由持有权限的账号发起并留痕。中间任何一环把明文缓存到本地前面的分离设计就白做了。3.3 散列值仍是一把万能钥匙跨用户关联分析为什么被整体禁掉第 6.5 条是整份规范里语气最强的一段所有节点禁止以两个或两个以上用户的个人信息进行关联分析包括以散列值进行分析并禁止对二级、三级数据做加工。然后给了三个例外。先看为什么连散列值都要管。设想两个系统各自按统一散列函数对证件号做散列各自都“没有明文”可一旦有人把两张表的散列列放在一起做等值连接同一自然人的两条记录立刻合并原本分散在不同系统里的画像瞬间拼完整。散列在这里起的作用不是保护而是提供了一个稳定的连接键。统一散列函数本来是跨节点比对的前提却顺带把这种连接能力标准化了。所以规范只能用流程禁令去约束这类“技术上完全可行”的操作。三个例外值得逐条看一是对个人信息做脱敏、散列化等安全加强处理——方向是让数据更难还原允许二是访问记录只用于异常登录等安全分析和处置——把行为数据的用途锁死在安全域禁止拿去做画像或者推荐三是允许对散列值进行加工比如二次散列、按域加盐因为那是在削弱连接能力而不是增强。判断标准其实只有一条这次加工是让数据更难关联还是更容易关联。4. 使用与脱敏50% 掩码阈值和临时明文窗口的设计账存储解决“数据躺着的时候安全”使用解决“数据被看见的时候安全”。这一块的规则密度最高也最容易被前端开发者当成“样式问题”处理掉。4.1 掩码不是打码每条规则都留了一个“可核对”的尾巴规范给的默认策略是系统中的二级以上隐私数据默认脱敏显示除手机号码外脱敏范围不少于 50%。然后逐条给了示例。把这些示例整理成规则表规律就出来了——每条掩码都刻意保留了某个“可核对”的片段字段掩码规则保留片段的作用姓名显示最后 1-2 个汉字如**明保持称呼可用能确认“是不是我”证件编号、社保卡号显示后 4 位如***********4432末位常含校验信息便于发现录入错误手机号前 3 后 4如133****4387可判断号段归属可人工核对证件有效期、失效期显示后 4 位如****0421保留月日能判断是否临近过期邮箱保留及之后如*********163.com域名可见可判断机构归属支付宝号、微信号显示后 4 位可区分同名账号户籍地址、居住地址、工作单位、发卡地长度大于 12 只显示前 6 位不足 12 位显示不超过 50%前缀对应行政区划层级门牌不可见50% 这个阈值的作用不是精确的数学界限而是给“只打一位星号”这类敷衍做法兜底。手机号被明确豁免是因为 11 位里隐藏中间 4 位只占约 36%——如果硬套 50%就得再砍掉前 3 位或后 4 位直接破坏业务上的可核对性。这条豁免说明制定者清楚脱敏的目标不是把字符数掩盖到某个比例而是在“不可辨识”和“可核对”之间找平衡点。地址那条规则尤其值得琢磨。长度大于 12 位只显示前 6 位对应的正是“省市区”这一层——保留到行政区划足以支撑业务判断比如判断是否需要异地办理而门牌号一旦露出来重识别难度会陡降。不足 12 位时按不超过 50% 截断是为了避免短地址被截成空串反而破坏可用性。写成代码大致是这样注意手机号那条分支不参与 50% 判定def mask(field: str, value: str) - str: if field real_name: # 保留最后 1-2 个汉字前面全部打码 return * * max(len(value) - 2, 1) value[-2:] if field in (cert_no, social_card_no): return * * (len(value) - 4) value[-4:] if field mobile: # 规范明确豁免隐藏中间 4 位即可 return value[:3] **** value[-4:] if field in (cert_valid_from, cert_valid_to): return **** value[-4:] if field email: local, _, domain value.partition() return * * len(local) domain if field in (address, employer, issue_place): if len(value) 12: return value[:6] * * (len(value) - 6) # 长地址只留行政区划前缀 keep max(len(value) // 2, 1) # 短地址最多露一半 return value[:keep] * * (len(value) - keep) return valuekeep max(len(value) // 2, 1)这一行是在守“不超过 50%”的下限与“至少露一个字符”的上限之间的夹缝partition()而不是split()是为了在邮箱格式非法时也能拿到原串而不是抛异常。真要排查脱敏问题先确认调用点在序列化层而不是模板层——只要接口还返回完整字段前端打多少星号都没有意义。4.2 读要双因子、写要实人全量显示的前置条件不对称规范允许二级以上数据全文显示但只开了两个口子用户注册时自己填写的内容可以全文显示用户查看和修改个人信息时需在二次认证后显示且二次认证级别不低于含手机短信验证码的双因子认证。修改信息“宜”使用实名等级四级也就是实人认证。这两条口子的风险完全不同。注册时回显的是用户刚刚亲手输入的内容没有引入任何新的信息暴露。而登录后查看个人信息面对的是“账号可能已被他人接管”的场景所以必须再验一次。至于修改规范用了“宜”而不是“应”但把等级抬到了实人认证——因为改手机号、改证件号这类动作的后果不可逆改完就能接管账号读和写的保护强度必须不对称。工程实现上有个细节容易被忽略二次认证的票据应该有独立的有效期和用途绑定不能和登录态共用一个 token。否则攻击者拿到登录态就等于绕过了二次认证这道门形同虚设。4.3 临时明文的三条通道1000 条、24 小时、8 小时、2 小时怎么来的第 3 章提到三级数据在互联网区只能存散列可实名核验必须拿原件去比对。这个矛盾只能靠“临时明文”解决需要的时候取出来用完立刻销毁。规范为此开了三条通道通道条数上限时限销毁点实名核验一次一条即时核验完成后销毁注册信息上报互联网区临时保存不超过 1000 条不超过 24h上报完成即销毁国家节点侧不超过 8h用户信息查询单用户接口不得支持批量收到结果后 2h 内处理处理完成即销毁明文这三组数字放在一起看会发现一个隐含的度量暴露面大致等于“条数 × 时长”。上报通道的条数最多所以给的时间窗是 24 小时且国家级节点收到后进一步压到 8 小时查询通道虽然是单条但它是“按需拉取”理论上可以被反复调用做批量爬取所以时限压到 2 小时并且在接口层明确规定“只允许接收和输出一个用户的个人信息不得存在一次查询多个用户的服务”。限制单次调用的返回规模比限制调用频率更能防住爬取——频率可以换 IP 绕单次返回结构绕不过去。还有一句容易被跳过的要求“一次实名核验只允许获取一条个人信息禁止使用超过实名核验需要的其他数据。”这实际上是在禁止“顺手多查几个字段”的常见写法——核验只要姓名和证件号就不要把手机号、地址一起捞出来。4.4 导出与销毁不可恢复要清到哪一层导出是明文离开受控环境的唯一合法出口所以规范对它的要求是“全流程留痕”。各节点要制定导出管理制度明确定义少量导出、批量导出、大批量导出的分级和对应审批流程明确责任人导出记录至少保存一年。记录内容列出的是数据导出用途、时间、操作地点、人员、授权信息以及导出文件大小、数据数量、所有个人信息的标识散列值。最后一项是关键设计——记录的是散列值而不是证件号。审计需要能回答“这次导出影响了哪些人”但审计日志本身不能变成新的泄露源。把标识换成散列既保留了可追溯性拿着某个人的散列去比对又避免了日志库成为明文聚集地。CREATE TABLE privacy_export_log ( id BIGINT PRIMARY KEY, purpose VARCHAR(200) NOT NULL, -- 导出用途需与审批单一致 exported_at TIMESTAMP NOT NULL, -- 导出时间 location VARCHAR(100) NOT NULL, -- 操作地点 operator VARCHAR(64) NOT NULL, -- 操作人员 auth_ticket VARCHAR(128) NOT NULL, -- 授权凭据编号 file_size BIGINT NOT NULL, -- 导出文件大小字节 row_count INT NOT NULL, -- 数据数量 subject_hashes TEXT NOT NULL, -- 涉及个人信息的标识散列值清单 retain_until DATE NOT NULL -- 至少保留一年 );subject_hashes用文本存清单是有意为之一次导出可能涉及成千上万人拆成关联表会让写入成本高到没人愿意填最后字段空着。retain_until落成日期而不是靠定时任务算是为了让“超期未归档”变成一条能直接查出来的 SQL 条件。销毁的要求更狠必须采用不可恢复方式保证不能从存储介质恢复且销毁后不得存在内存残留、缓存、临时文件、临时库表等剩余信息。这条直指“删除不等于销毁”的现实——数据库的DELETE只是解除引用WAL、页缓存、备份副本、临时表空间里可能还留着。落地时的检查清单通常包括临时表明确定义 TTL、核验流程结束即删、日志写入前先脱敏、备份介质的过期策略与主库同步。5. 落地排查五类最常见的翻车点规范读起来清晰落地时出问题的地方高度集中。下面五条是这类系统里反复出现的每条都按现象、原因、处理路径拆开。5.1 脱敏做在渲染层接口照样返明文现象很直观页面上显示的是133****4387抓个包响应体里是完整的 11 位号码证件号、地址一应俱全。原因通常不是开发者不懂规矩而是分层没定清楚——脱敏被当成了“展示逻辑”写在前端模板或者某个 view 层过滤器里API 契约本身仍然是全量字段。只要有人在浏览器之外直接调接口那层星号就不存在。处理路径是把脱敏下沉到序列化层或网关DTO 默认按字段策略输出掩码值只有明确列入白名单的接口比如用户本人二次认证后的查看接口才返回原文而且白名单要按字段粒度授权不能按接口整体授权。上面那段mask()应该被序列化框架统一调用而不是散落在各个 controller 里——散落就意味着漏一个。5.2 三级数据在互联网区存成了可逆密文现象库里存的确实是密文看着挺规范但用应用配置里的密钥一解证件号原样出来。原因往往是“以后可能要回显后四位”这种预留心态——先按可逆加密存着反正也加密了。可规范对三级数据在互联网区的要求是不可逆散列可逆加密在这里不算达标密钥一旦和数据库一起失守加密就等于没做。处理路径是拆字段散列主值承担比对职能末四位片段单独存成低级别字段承担展示职能完整号码只在临时明文通道里出现。迁移时要注意历史数据的处理顺序——先把片段回填完再把主值改成散列否则中间会出现一段“既不能比对也不能展示”的窗口期。5.3 统一散列函数被当成匿名化两库能直接 join现象两个系统都声称自己“不存明文只存散列”结果做数据核对时一条ON a.id_hash b.id_hash就把两个库连起来了联合结果里能还原出完整的个人画像。原因是对散列的性质理解偏了散列去掉的是“可读性”不是“可连接性”。同一输入必然得到同一输出加上规范要求用统一散列函数跨系统连接几乎是零成本的。处理路径分两层。流程层严格执行禁止跨用户关联分析的要求任何跨库匹配都要走审批并留痕技术层对非比对场景的散列按业务域加盐或二次散列让“能连”变成“连不上”。要提醒的是加盐会破坏跨节点比对能力所以只能用在本地去标识场景用于跨节点查询的那一份必须严格走统一算法。这两套散列最好在命名上就区分开避免有人拿错。5.4 临时明文只删了主表缓存和日志里还留着现象实名核验通道按要求“用完即销毁”主表确实空了可三天后排查问题时在临时表、Redis 缓存和请求日志里都能翻出完整证件号。原因是“销毁”被简化成了一条DELETE。而明文在链路上通常留了不止一份请求体进过网关日志、进过应用日志、进过缓存、进过数据库的临时表空间。任何一份漏掉前面的时限控制都失效。处理路径是把销毁当成一个跨层动作来设计临时表带 TTL 并由数据库自动清理缓存键带过期时间且不允许持久化日志在写入前统一过一遍脱敏尤其是异常堆栈里经常带着完整入参。再加一个定期扫描任务按正则去日志和缓存里找疑似证件号的串比等审计发现要主动得多。5.5 密钥管理员由实施方工程师兼任现象加密配置齐全算法也是规定的 SM2/SM4但上线半年后才发现持有密钥的那台设备和数据库的高权限账号掌握在同一批外部实施人员手里。原因很现实项目赶工期密钥初始化、日常运维、故障排查都是同一拨人做职责分离在文档里写了在人员安排上没落地。规范对此的要求其实非常硬——密钥管理人员不得从事数据操作、加工、使用且必须是本节点正式工作人员不得来自软件开发商、服务商。处理路径是把“谁同时握着两把钥匙”做成一个可检查的清单列出所有能访问密钥设备的人和所有拥有数据库高权限的人取交集交集必须为空。密钥操作走审批加双人复核应用侧只保留“密文进出”的调用能力不保留解密后的落地权限。这项检查建议在验收时做一次之后每季度复查一次因为人员流动会慢慢把这个交集重新填满。6. 设计取舍刚性定级表与业务弹性之间那道缝这份规范最值得学的不是那些数字而是它的结构用一张刚性的字段级定级表打底再用一条“禁止跨用户关联分析”堵住组合漏洞最后用“临时明文通道”给必须接触原件的业务开一个带时限的天窗。三层各管一件事互不越界。这种分层方式比“所有敏感数据一律加密”要难实现得多但它把安全成本压在了真正需要的地方。代价也很清楚主要是三处张力。第一处是三级数据在互联网区不可逆存储与“用户要看到自己证件号后四位”之间的冲突。规范没写解法工程上只能靠片段化存储来补而片段本身就是一小块可枚举信息。我的判断是这笔交易划算暴露四位数字远好过让整条记录处于可逆状态但前提是片段字段必须真的只用于展示不能被拿去参与任何匹配或查询条件——一旦它变成查询键就等于又造了一个弱化版的连接器。第二处是统一散列函数。它让跨节点比对成为可能也让散列成了全平台通用的连接键。规范用流程禁令去约束技术能力这在合规层面成立在工程层面需要额外手段兜底。我倾向的做法是跨节点比对用的散列和本地去标识用的散列在代码里就分成两个函数、两套命名让“拿错”变成编译期或启动期的错误而不是运行三年后才被审计发现。第三处是上报通道的“1000 条 / 24 小时”。地方节点在业务高峰期很容易撞上这个上限然后就有人想“先存到 2000 条反正是临时的”。这条线一旦松动后面的 8 小时、2 小时会跟着全部失效。可行的做法是把上限做进队列超过阈值就阻塞新任务配合分批加幂等重试消化积压宁可慢一点也不要让互联网区出现一个“反正很快就删”的明文池。验证这套设计有没有落地比读文档更有效的办法是拿生产库做两件事。一是导出一份真实字段清单逐个跑一遍“定级→存储方式→展示策略”看有没有字段掉进“无策略”的默认分支——那通常就是漏洞所在。二是写几条 SQL 扫全库找那些存着证件号格式、地址格式的 varchar 列看它们的加密状态和所在区域是否匹配。这两步花不了半天比任何自查表都直接。从那以后我每次接手这类系统都会先把字段策略表落成代码里的断言再让新字段无法绕过它入库——血泪经验是靠人记的规矩迟早会被一次“临时先这样”覆盖掉而断言不会。希望帮到你。本文还有配套的精品资源点击获取