简介《C0131国家政务服务平台统一身份认证隐私保护要求》是国家政务服务平台统一身份认证体系的重要规范文件面向政务平台建设者、系统运维人员及信息安全合规人员解决自然人身份信息在收集、保存、共享、销毁等环节的隐私保护与分级管控问题。文件依据多项国家标准与法规完整覆盖范围、引用文件、术语定义、隐私数据定级/判定/使用/责任及组织检查等章节其中一级、二级、三级数据的分级规则与附录A的自然人身份信息判定说明尤其具有实操指导价值。资源为1个PDF文件大小188KB可全文检索阅读便于按章节查阅数据使用基本原则与导出审批流程。已有815人学习下载适合开展政务系统隐私合规评估、统一身份认证方案设计或相关标准研究时作为权威参考。1. 先看这份政务隐私标准为什么“都加密了”照样被测评打回最近帮某开发商整改对接政务服务平台统一身份认证隐私保护要求的系统对方很委屈证件号码我用 AES 加密了手机号展示也做了脱敏为什么隐私合规评审还是打回问题恰恰出在“加密”上——这套标准里三级数据必须用不可逆加密保存可逆的 AES 不算数。这份编号 C0131 的 PDF 页数不多但条款密度很高数据怎么定级、怎么存、怎么显示、临时明文几小时内销毁、密钥归谁管全都有明确约束。它适合三类人正在对接统一身份认证的后端开发、负责政务系统隐私合规的架构师以及收到整改通知的运维。全文没有一句废话但坑全藏在条款缝隙里。2. 隐私数据定级与判定一级、二级、三级到底谁最敏感一张表看懂2.1 定级逻辑能不能对应到自然人是唯一分界线标准第 4 章先把隐私数据分成三级。很多人第一眼看到“一级”就以为它最敏感实际正好相反一级数据是不能对应到自然人实体身份的信息二级数据是能标识自然人实体、但相对公开的信息三级数据是能标识自然人实体、且隐私性较高的信息。也就是说级别数字越大保护要求越高。判断标准不是“我觉得这个字段敏感”而是“这个字段能不能单独或组合定位到具体的人”。例如用户性别、民族、国籍单独拿出来没法直接定位到某人所以是一级数据姓名、手机号可以定位到人但相对公开是二级数据证件编号、户籍地址、居住地址能精确到特定自然人而且隐私性强属于三级数据。定级时最忌拍脑袋一定要对照标准附录 A 一张表逐项标。这里的实战意义在于存储和展示逻辑完全跟着级别走。一级数据按常规安全要求处理二级数据需要加密存储、脱敏显示三级数据在互联网区必须走不可逆加密连可逆加密都不行。如果一开始定级就定低后面所有防护措施都会跟着降级测评时就是硬伤。2.2 附录A判定表把字段级别钉死在设计文档上标准附录 A 是一张“自然人身份信息隐私判定表”直接给出常见身份字段的隐私级别。我把它整理成下表做系统设计时可以直接复制到字段字典里信息隐私级别自然人姓名二级数据自然人登录账号二级数据自然人证件类型一级数据自然人证件编号三级数据证件散列码一级数据自然人手机号二级数据自然人实名等级一级数据证件有效日期二级数据证件失效日期二级数据自然人实名核验日期一级数据社保卡号三级数据发卡地二级数据用户邮箱二级数据注册时间二级数据用户生日二级数据用户性别一级数据用户学历二级数据用户支付宝号二级数据用户微信号二级数据用户户籍地址三级数据用户居住地址三级数据用户工作单位二级数据用户类型一级数据用户民族一级数据用户国籍一级数据用户第二手机号二级数据用户第三手机号二级数据这张表的价值在于它把模糊的“敏感信息”变成了可执行清单。我一般会在此基础上再建一张“字段合规登记表”列上字段名、隐私级别、存储方式、脱敏规则、责任人和检查结果评审时直接拿着它过。注意表中“证件散列码”是一级数据它是证件编号的散列值所以散列值本身被视为不能直接定位到人的信息。但要明白散列只是手段如果散列算法太弱或者无盐依然可能被撞库还原所以后端选型和实现不能因为“一级”就放松。2.3 判定规则儿童、生物特征、访问记录三个边界最容易定错标准第 5 章补充了几条特殊判定规则这三类最容易在项目里被忽略。第一未满 14 岁儿童的信息要提升隐私级别。标准原文是“可提升数据隐私级别”但在实际执行中我建议直接按提级处理。也就是说儿童的人脸、声纹本来按二级算一旦确认年龄不满 14 岁存储和展示就要按三级标准来约束。实现时用户模型里要有年龄或生日字段并在定级判断逻辑里加一个条件分支。第二生物特征数据是单独列出来的。人脸数据、声纹数据这类“较为公开”的生物特征属于二级数据指纹数据等可作为认定个人信息的生物特征属于三级数据。这里要注意人脸照片一旦和身份证号、姓名绑定实际上已经能精确定位自然人定级不能只看“人脸”这个字段本身要看整个对象。我见过有系统把人脸图片存 OSS 时只做访问控制没做加密这在标准下是不达标的。第三访问记录属于二级数据。个人用户在统一身份认证体系中的登录记录、政务门户访问记录等属于二级数据。很多人觉得日志不是个人信息但标准明确把它纳入了隐私数据范围而且限定用途——只能用于异常登录等安全分析和异常处置禁止用于其他用途。也就是说你在日志平台里做用户行为画像、点击流分析如果涉及这些访问记录就违规了。这个边界特别容易被产品经理忽略开发的时候要在数据字典上标明“仅用于安全分析”。判定规则还有一层意思定级要考虑“关联性”。一个字段单独看可能是一级但和另一个字段拼在一起能定位到人就要按能定位的级别来。标准 4.1 定义一级数据是“不能对应到自然人实体身份信息的数据”这个“不能”是指综合判断后的结论不是字段自述。所以定级文档里一定要写判定依据否则评审专家一问“为什么性别是一级、姓名是二级”你答不上来就尴尬了。3. 数据使用全流程落地从“先查再收”到“2小时销毁”的时限控制3.1 收集环节先查主节点查不到才允许本地收集标准第 6.2 节关于隐私数据收集实际上给了一个非常具体的前置约束地方和部门节点应优先查询并使用主节点已经存在的个人信息禁止重复收集用户个人信息仅当主节点无此用户信息时才可进行收集。这句话落到代码上意味着注册接口不能直接开一个表单让人填完落库。正确流程应该是用户发起注册时先调统一身份认证的查询接口用身份证号或用户标识去主节点查。如果主节点已经有这个人的信息就复用返回的数据不再让用户重复填写只有主节点返回“无此用户”才能进入本地收集流程。我一般会把这一步设计成注册入口的前置网关而不是在业务代码里写个“if exists else insert”因为还要考虑并发和重复提交。另外标准要求各节点在用户注册时提供隐私保障协议明示收集哪些信息并由用户明确确认。这里的“明确确认”不是默认勾选而是用户主动点击“同意”。很多系统把协议条款藏在注册页底部或者用复选框默认选中这都是不合规的。做前端交互时要改成强制手动勾选并在后端记录勾选时间和协议版本号方便出事后追溯。主节点可以通过两个途径收集个人信息用户注册与修改、地方和部门节点上传。所以地方节点上传数据前要先去重只上报本节点新增的用户不能把主节点已有的数据再传一遍否则既浪费带宽又可能因为数据口径不一致导致主节点同一人多条记录。3.2 保存策略二级加密、三级不可逆密钥归谁管标准 6.3 把存储要求按级别和网络区域拆得很细我整理成了一张执行表数据级别互联网区电子政务外网一级数据按国家相关安全原则及 GB/T 22239、GB/T 35273 执行无额外特殊要求同左二级数据应以加密方式存储应以加密方式存储三级数据应以不可逆加密方式存储禁止明文或可逆加密应以加密方式存储注意这里有个关键区别三级数据在互联网区必须“不可逆加密”也就是说你存进去的密文不能通过任何业务流程还原成明文。最常见的落地方式是散列存储比如对证件编号算 SM3 散列值查询时用散列值比对。而二级数据只要求“加密存储”可逆加密是可以接受的因为用户查看和修改个人信息时需要回显标准允许在二次认证后全文显示。密钥管理的要求同样硬性密钥必须存放在符合 GM/T 0051-2016 的密码系统或设备中由专人管理密钥管理人员不得从事数据操作、加工、使用等工作密钥管理人员应是本节点正式工作人员不得来自软件开发商、服务商等第三方机构。加密算法使用 SM2、SM4。这意味着你在代码里写的加密密钥不能直接放在配置中心供开发访问更不能把密码机口令交给外包运维。我做过一个模拟项目 X 时把证件号字段设计成了两个列id_no_hash存散列值hash_alg存散列算法标识查询时先算散列再比对全链路不回显明文。密钥管理这块甲方必须自己派人盯住密码机否则测评专家查人员名册时直接黄牌。3.3 显示规则脱敏比例、例外场景和二次认证标准 6.4 给出了非常具体的脱敏显示要求。我把它转成一张可抄作业的规则表字段脱敏规则示例姓名显示最后 1–2 个汉字**明公民身份号码 / 社保卡号显示后 4 位***********4432手机号码显示前 3 位和后 4 位133****4387证件有效日期 / 失效日期显示后 4 位****0421用户邮箱显示“”及之后部分*********example.com支付宝号 / 微信号显示后 4 位****0403户籍地址 / 居住地址 / 工作单位 / 发卡地长度大于 12 时显示前 6 位不足 12 位显示不超过 50%某市某区********除了手机号外二级以上隐私数据脱敏范围应不少于 50%。这里的“不少于 50%”是按明文长度算的不是按显示字符个数算。比如地址“某市某区某某街道某小区某栋某室”长度超过 12显示前 6 位剩下的全隐藏隐藏比例就超过 50%。如果地址恰好只有 8 个字显示不超过 50%也就是最多显示 4 个字。例外情况只有两种一是用户注册时填写的内容允许全文显示二是用户查看和修改个人信息时应在二次认证后全文显示。二次认证级别不低于包括手机短信验证码的双因子认证修改信息时宜使用实名等级四级实人认证进行认证。也就是说用户登录后直接进“个人中心”看到完整身份证号是不行的必须弹一个短信验证码或其他双因子步骤。很多系统做成了“登录后即可查看”这就是标准明确不允许的。除以上情况之外系统里默认必须脱敏显示二级以上隐私数据。所以你在设计前后端接口时要有一个“展示上下文”的概念同一个字段在用户注册回显、用户自己查看、运营后台查看三种场景下脱敏策略完全不同。运营后台原则上根本不应该看得到全文除非走审批授权。3.4 导出、归档与销毁时限不是建议是强约束标准 6.7 要求各节点制定数据导出管理制度明确二级和三级隐私数据对少量导出、批量导出、大批量导出的定级定义及管理流程明确责任人导出记录至少保存一年。记录至少包括数据导出用途、时间、操作地点、人员、授权信息以及导出文件大小、数据数量、所有个人信息的标识散列值。实操中我会把导出功能做成独立的审批模块没有审批单号则导出按钮不可用导出文件自动加水印和审计日志。标准 6.8 要求数据超出使用时限或完成生命周期后应归档或销毁。归档后形成离线文档不可在线查询、获取销毁要采用不可恢复方式保证不存在内存残留、缓存、临时文件、临时库表等剩余信息。这里的“不可恢复”意味着普通 delete 是不够的要覆盖或物理销毁存储介质。而且销毁后还要查一遍 Redis、日志文件、消息队列、binlog 里的残留。最容易被忽视的是 6.4 节里的临时明文时限三条都是硬数字地方和部门节点将本地注册用户信息临时保存到互联网区总数不得超过 1000 条时间不超过 24 小时上报后销毁。主节点收到上报数据后可在互联网区临时保存不超过 8 小时处理完成后销毁。地方和部门节点通过用户标识查询个人信息查询结果以明文下发查询发起节点应在收到信息 2 小时内完成处理并销毁互联网内的明文数据。我一般会用 Redis 的SETEX设置 TTL比如查询结果 7200 秒过期同时再跑一个兜底扫描任务防止 TTL 失效或者服务重启导致残留。这条经验放在第 4 章具体讲。4. 从条款到代码脱敏组件、SM3散列和临时数据销毁的实现细节4.1 脱敏组件按字段类型写一个工具类标准里的脱敏规则看上去简单但真正写代码时很容易因为边界条件翻车。比如身份证号长度不是固定 18 位老证件有 15 位名字可能只有一个字地址可能正好 12 个字符。所以我建议写一个独立的脱敏工具类所有展示层统一调用不允许在业务代码里手写字符串拼接。下面是一个 Python 参考实现def mask_identity(field: str, field_type: str) - str: if not field: return if field_type name: # 姓名显示最后 1-2 个汉字其他隐藏 keep min(2, len(field)) return * * (len(field) - keep) field[-keep:] if field_type id_card: # 身份证/社保卡保留后 4 位 return * * (len(field) - 4) field[-4:] if field_type mobile: # 手机号保留前 3 位和后 4 位固定中间 4 个星号 return field[:3] **** field[-4:] if field_type email: # 邮箱保留 及之后部分之前全隐藏 at field.find() if at 0: return * * len(field) return * * at field[at:] if field_type address: # 地址长度大于 12 显示前 6 位否则显示不超过 50% if len(field) 12: return field[:6] * * (len(field) - 6) keep int(len(field) * 0.5) return field[:keep] * * (len(field) - keep) return * * len(field)逻辑说明姓名部分兼容单字名min(2, len(field))保证单字名也至少显示那个字但实际业务中单字名全显示可能不合规标准说“显示最后 1-2 个汉字”没有明确单字情况稳妥起见应该全部隐藏或者按最后 2 位只显示 1 位。邮箱分支处理了没有的异常避免find返回 -1 导致负数切片。地址分支严格按“长度大于 12 显示前 6 位否则显示不超过 50%”实现注意这里len对中文字符是按字符计数的符合标准里的“长度”概念。参数说明field_type支持name、id_card、mobile、email、address五种其他未知类型一律全隐藏采取“宁可显示少不可显示多”的策略。生产环境中建议把脱敏后的字符串长度和原始长度做成测试用例回归尤其是地址长度恰好等于 12 和 13 的边界很容易写错。4.2 不可逆散列存储字段设计与散列函数三级数据在互联网区必须用不可逆加密方式存储最常规的做法是 SM3 散列。但直接对证件号做sm3(id_card)有一个风险如果证件号码空间有限攻击者可以构造字典把所有号码的散列值算出来反向匹配你的密文。所以实际落地时一般会加随机盐或者使用主节点统一提供的散列函数。标准原文说的是“应采用主节点统一提供的散列函数对明文进行散列运算”也就是说你不能自己随便选一个散列算法完事必须按统一要求来。下面的代码只是本地概念验证用于理解字段怎么存import hashlib import os def sm3_hash_with_salt(raw: str, salt: bytes None) - str: 将明文用 SM3 随机盐散列返回 salt$hex$digest if salt is None: salt os.urandom(16) digest hashlib.new(sm3, raw.encode(utf-8) salt).hexdigest() return fsalt${salt.hex()}${digest}逻辑说明os.urandom(16)生成 128 位随机盐每个用户独立把盐拼在明文后面做 SM3 散列输出格式是salt$盐的十六进制$散列值。查询时从库里取出这个完整字符串解析出盐再用同样方式重算散列做比对。这样即使两个用户证件号后四位相同散列值也完全不同。参数说明盐长度 16 字节碰撞概率极低hashlib.new(sm3)依赖 OpenSSL 是否编译了 SM3 算法如果环境不支持可以用gmssl库替代写法是from gmssl import sm3, func; sm3.sm3_hash(func.bytes_to_list(raw.encode()))。无论用哪种记住标准要求的是“国家节点统一提供的散列函数”如果所在项目有统一下发的 SDK 或者密码机接口优先调用那个不要用自己拼的散列方案代替。数据库里证件编号字段应该设置成varchar(80)左右因为salt$16字节hex$32字节hex这个字符串长度接近 90 字符别只留 32 位。4.3 传输与密钥管理调用统一信任服务平台接口的参数要点标准 6.4 提到所有涉及未加密个人信息数据的传输要按 C 0111-2018 的 8.3、C 0113-2018 第 7 章中安全通讯的规定执行互联网和电子政务外网传输均应按此处理。也就是说地方节点向主节点查个人信息时请求报文不能裸奔。常规做法是身份认证、时间戳、随机数、签名再加上报文加密。下面是一个简化版的请求构造示例import time import uuid import json import hmac import hashlib def build_secure_request(app_id: str, secret_key: str, payload: dict) - dict: timestamp str(int(time.time())) nonce uuid.uuid4().hex body json.dumps(payload, sort_keysTrue, ensure_asciiFalse) sign_str f{app_id}{timestamp}{nonce}{body} # 实际场景按标准要求用 SM2 签名这里用 HMAC 说明参数结构 sign hmac.new( secret_key.encode(), sign_str.encode(), hashlib.sm3 if hasattr(hashlib, sm3) else hashlib.sha256 ).hexdigest() return { app_id: app_id, timestamp: timestamp, nonce: nonce, sign: sign, data: body, }逻辑说明timestamp用于防止请求重放服务端通常会校验时间差在 5 分钟以内nonce要求全局唯一服务端会把已用 nonce 存一段时间重复请求直接拒绝sign_str把 app_id、时间戳、nonce、请求体拼接起来做签名保证请求没有被篡改。参数说明secret_key绝不能出现在代码日志或配置仓库里应该从密码机或 KMS 的接口动态获取生产环境签名推荐 SM2报文加密推荐 SM4这里用 HMAC 只是为了演示参数结构。另外要注意标准里“查询接口只允许接收和输出一个用户的个人信息不得存在一次查询多个用户的服务”所以这个请求的payload中只能有一个用户标识不能是列表。服务端也要做校验请求体里一旦出现数组参数直接抛异常。4.4 临时数据2小时销毁定时任务怎么设计查询结果明文 2 小时内销毁这个“2 小时”不能依赖开发人员记得删要用技术手段强制过期。我的做法是 Redis TTL 加兜底扫描双保险。import json import redis import time r redis.Redis(host10.0.0.5, port6379, db0) def store_temp_query(request_id: str, user_data: dict): 把查询结果临时存入 Redis2 小时后自动过期 key ftemp:query:{request_id} r.setex(key, 7200, json.dumps(user_data, ensure_asciiFalse)) def sweep_temp_queries(): 兜底清理每小时扫描一次删除无 TTL 或已过期的临时数据 for key in r.scan_iter(temp:query:*): ttl r.ttl(key) if ttl is None or ttl 0: r.delete(key)逻辑说明setex是“set expire”原子操作直接给 key 设置 7200 秒2 小时过期时间。为什么还要兜底扫描因为 Redis 在某些异常情况下可能没有正确设置 TTL或者服务扩容时数据迁移导致 TTL 丢失。sweep_temp_queries遍历所有temp:query:前缀的 key如果发现没有 TTL 或者 TTL 是 -1永不过期立即删除。参数说明7300 这个数字就是标准里的“2小时”如果你们项目审计要求按分钟记可以改成2 * 60 * 60不要写魔法数字。request_id要关联到一次具体的查询操作方便追溯。扫描任务建议用独立定时器跑不要挂在业务进程的线程池里避免高峰期被挤掉。另外除了 Redis还要检查本地临时文件、应用缓存、消息队列里是否有明文积压定时任务只能清存储清不了已经发出去的响应。5. 避坑统一身份认证隐私保护最容易翻车的五个细节5.1 把“加密存储”当成“不可逆加密”测评直接打回现象系统把证件编号用 SM4 加密后存进数据库认为“已经加密了”评审结果却是“不符合”。原因标准 6.3 明确规定三级数据在互联网区应以不可逆加密方式存储不得以明文或可逆加密方式进行存储。SM4 是对称加密密钥在手就能还原属于可逆加密不能用来存三级数据。解决把证件编号改为散列存储使用主节点统一提供的散列函数或者按 4.2 节的方式加盐散列。检查方法看数据库中该字段是否能用应用密钥解密回原文如果能就算不合规。记住这里要的是“不可还原”不是“不可读”。5.2 日志把脱敏打回原形现象页面上手机号显示133****4387但运维查日志时看到完整手机号13312344387日志审计阶段被查出。原因页面调用了脱敏接口但业务代码里logger.info(用户手机号: user.mobile)直接打印了原始 DTO 字段日志系统把入参、出参全量记录了。解决统一日志过滤器在 Logger 层对敏感字段做脱敏或者禁止在业务日志中打印敏感字段。还要注意异常堆栈里可能带方法入参需要在网关层统一清理。我习惯在日志框架里配一个MaskingConverter对所有包含mobile、idNo、email的字段值做正则替换。5.3 查询接口做成批量一次查多个用户现象地方节点对接主节点时为了“提高效率”把个人信息查询接口设计成POST /queryUserList参数是userIdList一次能查 100 人。原因开发觉得按用户标识循环调用太慢图省事做了批量接口。解决标准明确“查询接口只允许接收和输出一个用户的个人信息不得存在一次查询多个用户的服务”。必须改成单用户查询接口参数只有一个用户标识即便内部有批量需求也要在服务端强制循环单查并限制并发。测评时专家会专门检查接口入参格式发现数组直接扣分。5.4 临时明文数据超时残留现象用户实名核验的明文数据存在本地临时表核验完成后没删两天后还能从管理后台查到。原因定时清理任务没部署或者清理逻辑只在页面关闭时触发用户关浏览器根本没执行。解决所有临时明文入库时都带过期时间字段并配合 Redis TTL 或数据库定时任务上线前用模拟数据实测 2 小时后是否自动消失。可以用第 4.4 节的兜底扫描思路每小时扫一遍全库凡是超过时限的临时表记录立即物理删除不能只软删除。5.5 密钥管理员来自外包商现象某项目的密码机口令、密钥备份全在集成商运维手里甲方没有一个人能独立操作密码机评审时被质疑。原因甲方人手不足把密钥管理工作一起委托给了厂家图省事。解决标准要求密钥管理人员应是本节点正式工作人员不得来自软件开发商、服务商等第三方机构而且密钥管理人员不得从事数据操作、加工、使用等工作。实际操作上甲方至少要指定两名正式员工轮值一人管密码机口令一人管应用接入配置双人操作要留审计日志。测评时专家会查人员名册和密码机操作记录第三方厂商人员的操作记录会被重点标记。6. 怎么自查这份标准的落实情况一份可执行的验证清单6.1 字段等级逐项打勾第一件事把附录 A 的判定表下载下来对照你们系统的用户表、日志表、缓存表每个字段标记等级。重点看那些“看起来不像个人信息”的字段注册时间、用户类型、发卡地、证件有效日期它们都有明确级别漏标等于没有做过隐私设计。建议做一张“字段合规登记表”列字段名、所在表、隐私级别、存储方式、脱敏规则、是否二次认证可显、检查结论。这张表就是整改验收的底稿。每次评审前我会拿它跟数据库information_schema的字段清单做比对看有没有新增字段没有定级。6.2 边界脱敏样例回归光看规则容易踩坑要用测试用例验证。我常用的边界样例至少包括姓名长度为 1 和 2单字名应该全部隐藏还是显示标准没写死我建议按最严处理全部隐藏避免“某”字暴露。手机号位数不是 11 位比如座机或虚拟号脱敏逻辑要能容错不能因为索引越界报错。地址长度恰好等于 12标准说“长度大于 12 只显示前 6 位不足 12 位显示不超过 50%”等于 12 既不属于大于 12也不属于不足 12怎么办稳妥做法是归入“不足 12”那一档显示不超过 50%即最多 6 位。邮箱没有直接全隐藏别返回原串。测试用例要用参数化跑覆盖None、空字符串、纯数字、带空格等各种输入脱敏函数必须保证不抛异常。6.3 临时数据生命周期实测最后做一个全链路演示模拟一次用户信息查询记录查询时间把明文数据放入 Redis 和临时表等 2 小时后再跑一次清理任务检查所有位置是否还有残留。重点检查四个地方业务数据库临时表、Redis、应用日志、消息队列。如果日志里打了完整入参即使存储层清了日志文件里还是会有明文所以日志过滤要在清理测试之前就配置好。我还习惯在临时数据写入时打一个“到期时间”标签比如 Redis key 的 TTL 还有多少秒用监控面板展示让评审专家直观看到这条数据会在 2 小时后消失比口头解释有说服力。从那以后我每次做统一身份认证系统的隐私评审都会强制走一遍这个流程先打开这份 PDF对照附录 A 逐字段过定级再写一段脱敏边界测试最后设一个凌晨两点的定时任务去扫临时明文。很多时候不是标准没写清楚是我们根本没把标准里的数字当硬指标。希望帮到你。本文还有配套的精品资源点击获取