等保三级整改实战:3人小队用Cursor一周搞定数十个系统的敏感数据加密

发布时间:2026/9/25 13:45:51
等保三级整改实战:3人小队用Cursor一周搞定数十个系统的敏感数据加密 1. 等保三级整改里敏感数据加密为什么总卡在“改不完”等保三级复测的整改清单里敏感数据加密几乎是必选项手机号、身份证号、银行卡号、家庭住址只要落库就得加密。算法本身不难AES-256 是成熟方案难的是“改哪些、怎么改、存量怎么办”。我们这次面对的是数十个遗留系统、十几个数据库、上百张表、十几个独立代码仓库整改窗口只有一周团队三个人。真正拖慢进度的从来不是加密算法而是三件事第一敏感字段散落在上百张表里不逐张翻就不知道哪张有第二存量明文有几百万条服务不能停新老格式必须兼容第三系统之间共用用户数据A 系统改了加密逻辑B 系统的查询可能直接挂掉。这三件事叠加人工摸排加改造保守估计要两周以上。这篇把我们实际跑通的路径完整写出来用 Cursor 做批量扫描和重复代码生成把加解密收拢到 MyBatis 拦截器一层业务代码只加注解。你可以直接复制提示词模板、拦截器骨架和逐系统验证清单目标是一周内完成整改并留下可审计的验证记录。适合中小团队在数十个遗留系统上快速补齐 AES-256 加密能力。2. 前置准备TaoToken 接入与 Cursor 里的模型配置批量扫描字段、生成拦截器骨架、写迁移脚本这些活都需要一个稳定的模型调用入口。我们用的是 TaoToken它提供 OpenAI 兼容接口在 Cursor 里配置成自定义模型即可不用改代码里的调用方式。先拿到 API Key。打开 https://taotoken.net/api-keys 登录后创建一个 Key复制保存。注意 Key 只在创建时完整显示一次丢了就重新建一个。然后在 Cursor 里配置。打开 Settings找到 Models 面板添加一个 OpenAI 兼容的模型提供方{ modelProvider: openai, apiKey: 你的TaoToken API Key, baseUrl: https://taotoken.net/api/v1, models: [ claude-sonnet-4-20250514, gpt-4o ] }配置完成后在 Cursor 的 Chat 面板里选这个模型发一句“你好”验证连通。如果返回正常说明接入成功。这一步建议在第一天上午做完后面所有扫描和生成都依赖它。注意baseUrl 末尾要带/v1这是 OpenAI 兼容接口的约定路径。少写这一段Cursor 会报 404。如果你更习惯在网页里直接对话验证模型效果可以打开 https://taotoken.net/model-chat 先试几轮确认模型对 Java、MyBatis 这类技术问题的回答质量符合预期再回到 Cursor 里批量用。3. 第一天用 Cursor 批量扫描把“要改哪些”摸清楚3.1 数据库层导出字段清单让模型做语义识别第一步不是写代码是把所有表的字段名导出来。写一个脚本连接十几个数据库把库名.表名.字段名全部导出成文本。然后把这批文本喂给 Cursor用下面这个提示词以下是我们所有数据库的表字段清单请识别出可能包含敏感信息的字段。 敏感信息包括手机号、身份证号、银行卡号、姓名、地址、邮箱、IP地址。 请按“数据库名.表名.字段名”的格式列出并标注敏感类型。 [粘贴字段清单]半小时左右能拿到一张疑似清单。关键是人工逐条确认模型会误判比如user_card_type存的是会员卡类型不是银行卡号。我们最终确认涉及敏感字段的表 87 张字段 143 个比预估多不少。如果靠人工逐张翻这一步就要两三天而且不敢保证不漏。3.2 代码层按五个类型归类使用位置有了字段清单还要知道每个字段在代码里被哪些地方读写。用 Cursor 逐个仓库做全局搜索搜索维度是字段名、实体类名、以及 WHERE 条件里用到敏感字段的 SQL。每扫一个仓库让模型把结果归成五类类型场景处理方式AINSERT/UPDATE 写入写入前加密BSELECT 读取后处理读取后解密CWHERE 条件中使用需确定性加密索引D日志打印中出现脱敏E接口返回值直接透传脱敏或加密传输类型 C 最麻烦。加密之后WHERE phone 138xxxx就没法用了数据库里存的是密文不能用明文比对。我们的处理是用于查询的敏感字段额外维护一个确定性加密索引同样的明文始终加密成同样的密文专门用于查询存储字段用随机 IV 的 AES保证安全性。这个决策必须在第一天定下来否则后面改到一半返工。3.3 汇总成改造清单按系统分工扫完所有仓库得到一张完整清单标注每个系统涉及哪些库、哪些表、哪些字段、哪些仓库要改。到这里“要改哪些”才真正清楚。整个摸排花了一天纯人工保守估计三到四天。4. 第一天下午到第二天把加解密收拢到 MyBatis 拦截器4.1 核心决策不在每个 Service 里手动加解密143 个字段散在数十个系统、几十个 Service 里如果每个读写点手动加解密要改几百处改一个漏一个上线必出事。我们选的方案是在 MyBatis 层做统一拦截注解驱动业务层无感知。业务代码唯一要动的就是在实体类字段上加一个注解SensitiveField(type EncryptType.RANDOM) private String phone;前期多花一天搭基础设施后面 143 个字段的改造成本会低到难以置信。改造分三阶段基础设施搭建、字段改造、存量数据迁移。顺序想清楚三个人才能并行不互相踩。4.2 加密工具类随机 IV 与确定性双模式给 Cursor 的提示词帮我实现一个 AES-256 加密工具类要求 1. 支持两种模式 - 随机 IV 模式用于存储同明文每次密文不同 - 确定性模式用于查询索引同明文始终同密文 2. 密钥从配置中心读取支持密钥轮换同时支持新旧两个密钥解密 3. 加密结果 Base64 编码方便数据库存储 4. 加密结果加固定前缀标识用于迁移期间区分明文和密文 5. 所有异常封装成业务异常不能把加密细节暴露给上层 6. 技术栈Java 17 Spring Boot 3生成后重点 review 两处密钥轮换的优先级顺序以及异常日志不能打印原始数据。这两处模型容易写得不严谨。4.3 MyBatis 拦截器骨架Intercepts({ Signature(type Executor.class, method update, args {MappedStatement.class, Object.class}), Signature(type Executor.class, method query, args {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}) }) public class SensitiveFieldInterceptor implements Interceptor { private final MapClass?, ListField fieldCache new ConcurrentHashMap(); Override public Object intercept(Invocation invocation) throws Throwable { Object[] args invocation.getArgs(); MappedStatement ms (MappedStatement) args[0]; Object parameter args[1]; if (ms.getSqlCommandType() SqlCommandType.INSERT || ms.getSqlCommandType() SqlCommandType.UPDATE) { encryptFields(parameter); } Object result invocation.proceed(); if (ms.getSqlCommandType() SqlCommandType.SELECT) { decryptResult(result); } return result; } private void encryptFields(Object parameter) { // 遍历参数对象命中 SensitiveField 的字段按类型加密 // RANDOM 用随机 IVDETERMINISTIC 用确定性模式 } private void decryptResult(Object result) { // 处理单对象、List、以及 MyBatis-Plus 分页结果 // 读取时判断是否有加密前缀无前缀直接返回明文兼容期 } }生成后重点看两个地方反射缓存的并发安全性用ConcurrentHashMap没问题分页结果的解密是否遗漏测几个边界 case。下午三点基础设施完成单元测试覆盖率 83%。在测试库的实体类加一个注解写进去是密文读出来是明文查询和分页都正常。5. 第三天到第五天数十个系统并行改造基础设施就位后三个人按系统并行每个字段的标准流程固定下来在实体类字段加SensitiveField注解约 1 分钟一个字段。检查该字段是否用于 WHERE 查询如果是同步处理查询索引字段。检查日志打印是否包含该字段如果是加脱敏。检查接口返回值是否直接透传如果是确认是否需要脱敏。跑单元测试。跑集成测试含存量数据兼容性验证。Cursor 在这里的价值不是生成复杂逻辑而是批量处理高度重复的工作。把实体类喂给模型让它按清单加注解、处理边界人来 review。单个实体类平均用时从估计的 30 分钟压到 8 分钟。143 个字段三人并行三天全部改完集成测试通过。中间踩的一个坑有个老系统的接口把手机号直接拼进 SQL 字符串String sql SELECT * FROM user WHERE phone phone ;这种情况拦截器拦不到。让 Cursor 在该系统的所有仓库里搜类似写法找到 5 处全部改成参数化查询同时加上查询时的加密转换。这 5 处如果靠人工逐文件找很可能漏掉。6. 第六天存量数据迁移最危险的一步几百万条明文散在十几个数据库里要在不停机的情况下全部迁成密文。我们选双读兼容加分批迁移。拦截器里内置的读取逻辑是读出数据判断是否有加密前缀有前缀就解密返回无前缀直接返回明文兼容存量。迁移脚本的核心逻辑让 Cursor 生成但迁移策略自己设计迁移脚本要求 1. 按数据库逐个跑互不干扰 2. 每次迁移 1000 条间隔 500ms 控制数据库压力 3. 每批迁移完做校验解密后和原文比对 4. 支持断点续跑 5. 出错自动暂停等人工介入先在测试库完整跑一遍验证数据一致性解密校验零错误再上生产。生产库按数据库分批选在凌晨流量低谷执行全程监控。早上六点所有数据库迁移完成。7. 第七天回归、日志审查与整改报告最后一天三件事。全量回归测试所有涉及敏感字段的接口全部过一遍。日志审查确认没有任何接口还在日志里打印明文敏感数据。整改报告把加密方案、改动范围、测试结果整理成文档提交合规团队。复测当天审计方抽查数据库所有敏感字段密文存储查询正常接口响应正常。逐系统验证清单可以按这个表逐项打勾留下可审计记录检查项验证方式通过标准字段已加密直连数据库查该字段值为密文且带前缀读取正常调接口查该字段返回明文格式正确查询正常用该字段做条件查询能命中结果正确日志无明文搜日志文件无明文敏感值接口无透传抓包看返回值按需脱敏或加密存量已迁移抽样比对解密后与原文一致8. 本篇常见错排查报错一Cursor 里模型调用返回 404。检查 baseUrl 是否写成https://taotoken.net/api/v1末尾的/v1不能少。如果还是 404去 https://taotoken.net/api-keys 确认 Key 是否有效、额度是否充足。报错二拦截器不生效写进去还是明文。先确认拦截器是否注册成 Spring BeanMyBatis 的Interceptor需要显式注册。再确认实体类字段上的注解是否被正确扫描注解的Retention必须是RUNTIME。报错三查询条件用密文字段查不到。这是类型 C 的典型问题。用于查询的字段必须用确定性加密且查询时把明文转成确定性密文再拼进 SQL。如果用了随机 IV同样的明文每次密文不同永远查不到。报错四迁移后部分数据解密失败。大概率是密钥轮换没配好。检查解密时是否同时尝试新旧两个密钥以及迁移脚本用的密钥和拦截器解密用的密钥是否一致。报错五分页查询结果里敏感字段没解密。MyBatis-Plus 的分页结果是包装对象拦截器要单独处理IPage类型不能只处理 List 和单对象。9. 把架子搭对再让 AI 加速复盘下来AI 提速的不是思考是执行。加密工具类怎么设计密钥轮换、拦截器要不要缓存反射结果、迁移用双读还是停写、系统之间改造顺序怎么排这些判断是我们自己做的。AI 做的是把这些判断落成代码的速度快了三四倍同时帮我们减少了遗漏——那 5 处手拼 SQL、日志里的明文、接口透传的字段散在各个角落人工找成本极高。如果你也面对等保三级整改两条建议先摸清楚要改哪些再动手用 AI 批量扫比人工快三倍以上把加解密收拢到一处不要分散在业务代码里。架子搭对了后面的事 AI 能帮你快三四倍架子搭错了AI 只会帮你更快走向错误方向。接入和排障相关的操作可以对照 https://taotoken.net/doc 里的接口说明需要长期跑编码和 Agent 任务的团队可以看 https://taotoken.net/coding-plan 的额度方案日常验证模型对 Java、MyBatis 问题的回答质量直接在 https://taotoken.net/model-chat 里试几轮就行。