Skip to content

模块导航: 总入口 · 总纲与方法 · 后端场景 · AI与算法工程 · 国企与金融 · 网络问答


193. 手机号/身份证/支付密码,存储和传输怎么加密(敏感数据加密) ​

【考察内容】数据安全与合规

【题目】合规要求用户手机号、身份证、支付密码必须加密。存储时用什么算法(哈希/加盐/可逆加密/国密)?不同数据(密码 vs 手机号)加密方式有何不同?传输层(HTTPS、字段级加密)怎么做?

【参考答案】

  1. 分类分级:先明确哪些是敏感数据(个人信息、凭证、支付信息),分级管理;

  2. 传输加密:全站 HTTPS(TLS 1.2+),防中间人窃听与篡改;

  3. 存储加密(按数据性质选算法):

    • 密码:不可逆哈希+盐(BCrypt/SCrypt/PBKDF2),禁止 MD5/SHA1 裸存,禁止可逆加密存密码;
    • 身份证/手机号等可逆数据:AES-256 对称加密存储(密钥托管 KMS/HSM),配合脱敏展示(135****6789);
    • 支付密码:与登录口令同类,属鉴别信息,应不可逆哈希+盐(BCrypt/Argon2,或硬件 PIN 加密机内的等价值保护),不得可逆加密存储;HSM/KMS 只用来保护加密数据用的密钥,不要写成"支付密码加密存储";
  4. 脱敏:展示/日志/接口返回时脱敏(手机号、身份证中间打码)——防泄露扩散;

  5. 密钥管理:密钥与数据分离(KMS)、定期轮换、密钥不硬编码(配置中心/环境变量加密);

  6. 传输层:App 与 Server 间可再加应用层加密(防抓包)——敏感接口二次签名/加密;

  7. 合规:日志脱敏(不能打完整手机号)、数据导出审批、加密策略审计。

  8. 容量估算: 加密开销:AES-GCM 加解密通常 μs 级,国密 SM4 同数量级;HTTPS 握手在开启会话复用后额外 RTT 可接受。密钥管理:信封加密,DEK 由 KMS 管理;备份与轮换演练。存储:密文略长于明文,分字段加密避免全表加密性能问题。检索需求用密文哈希索引。

  9. 失败与降级: KMS 不可用时:核心链路缓存 DEK 有限时间+告警,或降级暂停写敏感数据;禁止明文临时落盘。密钥轮换失败要有回退到上一版本密钥的能力。

【原理溯源】

  • 为什么密码必须不可逆哈希而手机号用可逆加密? 密码的用途是“验证你知道它”,系统永远不需要还原明文——不可逆哈希即使库被拖走也无法直接得到原密码。手机号/身份证的用途是“读取与展示”(客服核身、短信下发),必须可还原,所以用可逆对称加密。选算法前先问:业务需不需要还原明文。

  • 为什么 MD5/SHA1 不能存密码? 它们设计目标是快速摘要(微秒级),GPU/彩虹表可在短时间内暴力破解百亿组合。BCrypt/SCrypt/PBKDF2 是故意慢的密码哈希(含工作因子/内存硬化),把暴力破解成本抬高几个数量级。加盐(per-user salt)则让预计算彩虹表失效——相同密码不同用户哈希不同。

  • 为什么密钥必须与数据分离(KMS/HSM)? 密钥写在代码/配置里,代码泄露=密钥泄露=加密形同虚设。KMS 让密钥在独立系统中生成与使用,应用只拿“用密钥加密的结果”或调用加解密接口;HSM 进一步保证密钥永不出硬件。轮换密钥时只需重新包装 DEK,不必重加密全库(信封加密)。

  • 为什么要脱敏展示? 加密解决“存储安全”,脱敏解决“展示面扩散”。客服系统、日志、BI 导出、前端页面若都显示完整手机号,任一环节泄露都是明文泄露。脱敏(135****6789)在“可用”与“最小暴露”间取平衡——客服可核对前后位,但拿不到全号。

  • 国密与通用算法如何选? 金融/政务/等保场景可能强制 SM2/SM3/SM4;一般互联网业务 AES-256 + SHA-256/BCrypt 足够。关键是算法选型合规、密钥管理规范,而不是“用了很酷的算法但密钥在 Git 里”。

  • 元理论对应: 原理 7「对抗理性与可观测的认识论」(题群见《核心考点与元理论》§7.7)

【选型判断树】

text
敏感数据怎么保护?
1. 业务需要还原明文吗?
   ├─ 不需要(密码/支付密码)→ 不可逆哈希+盐(BCrypt/Argon2)
   └─ 需要(手机号/身份证/银行卡)→ AES-256 可逆加密 + KMS
2. 密钥放哪?
   └─ KMS/HSM,禁止硬编码;信封加密便于轮换
3. 哪里还会露出明文?
   ├─ 页面展示 → 脱敏
   ├─ 日志 → 脱敏/不落盘(见 196)
   ├─ 接口返回 → 按需脱敏
   └─ 传输 → HTTPS + 必要时字段级加密
4. 合规场景
   └─ 金融/政务评估国密(SM2/3/4)

判断口诀: 密码只哈希,身份要加密;密钥进 KMS,展示必脱敏。

【口述骨架】(5 分钟;框架 C,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“按是否需还原明文分两类:密码哈希、身份加密”
0:30–1:30算法选型BCrypt 存密码;AES 存可逆字段;禁 MD5 裸存
1:30–2:30密钥管理KMS/HSM、信封加密、轮换、禁硬编码
2:30–3:30展示与日志脱敏135****6789;多层露出面治理
3:30–4:30传输HTTPS + 敏感接口二次加密
4:30–5:00收尾“算法要对,密钥要管,露出面要收”

【关键数字】

参数经验值说明
密码哈希Argon2id / BCrypt cost≥10 / PBKDF2-HMAC-SHA256 60 万轮(OWASP 现行值,10 万已偏低)故意慢
对称加密AES-256-GCM具备完整性校验
哈希摘要SHA-256+盐禁 MD5/SHA1 存密码
脱敏规则手机号保留前3后4;身份证保留前6后4按业务可调
密钥轮换90–180 天信封加密降低轮换成本
TLS 版本1.2+(推荐 1.3)禁 SSLv3/TLS1.0

【追问链】(三层)

L1|“为什么密码要加盐?” → 防彩虹表与相同密码关联。每个用户一个随机盐,相同密码哈希结果不同;攻击者无法用预计算表批量破解,只能对每个用户单独爆破,成本乘以用户数。

L2|“加密后的手机号怎么按号段查询?” → ① 用确定性加密(同明文同密文)建立等值索引,但会泄露相等关系;② 保存密文+可搜索加密/盲索引;③ 密文等值查询用的“哈希索引”本质上还是全局固定 pepper 的确定性 HMAC(每行独立随机盐的哈希查询端算不出同一摘要,无法等值匹配),所以它与 ① 是同一方案的两个名字,且同样泄露相等关系、手机号域仅约 4×10¹⁰ 可整域穷举——pepper 必须放 KMS/HSM,或改用盲索引/可搜索加密。按查询场景权衡。

L3|“密钥泄露了怎么办?” → ① 立即吊销/轮换密钥;② 评估用该密钥加密的数据范围;③ 用新密钥重新加密受影响数据;④ 排查泄露渠道(Git 提交、日志、核心转储);⑤ 若涉及个人信息,按个保法评估是否告知用户。事前:KMS + 扫描硬编码密钥 + 权限最小化。

【评分标准】

档位答案特征
60 分知道“密码加密存、传输用 HTTPS”
80 分区分哈希 vs 可逆加密;KMS;脱敏
95 分论证“是否需还原”的决策;密钥分离与信封加密;多露出面治理;合规国密

【关联题】

【自测】

  1. 为什么不用 AES 存用户密码? 参考答案:AES 可逆,库泄露则密码明文暴露。密码只需验证不需要还原,应使用 BCrypt 等故意慢的不可逆哈希+盐。
  2. 密钥写在配置文件里有什么问题? 参考答案:代码/配置泄露即密钥泄露,加密失效。应使用 KMS/HSM 管理,密钥与数据分离。
  3. 加密存储后接口返回还要脱敏吗? 参考答案:要。加密保护存储面,脱敏保护展示面。日志、页面、导出任一处露出全号都会造成泄露扩散。

194. 抓包发现密码是明文,要上 HTTPS(HTTPS/TLS) ​

【考察内容】HTTPS 原理是网络/安全高频题

【题目】安全测试报告指出:App 登录请求走 HTTP,抓包能看到明文密码。要改造 HTTPS。HTTPS 比 HTTP 多做了什么?TLS 握手过程(证书校验、密钥交换、会话密钥)讲一下?中间人攻击怎么防?

【参考答案】

  1. HTTPS = HTTP + TLS:提供加密(防窃听)+ 完整性(防篡改)+ 身份认证(防冒充);

  2. 握手流程(TLS 1.2 简化):

    • 客户端发送 ClientHello(支持的 TLS 版本、加密套件、随机数 A);
    • 服务端返回 ServerHello(选定套件、随机数 B)+ 证书(含公钥、域名、CA 签名);
    • 客户端验证证书(CA 信任链、域名、有效期、吊销状态)→ 生成预主密钥,用服务端公钥加密发送(RSA 模式;ECDHE 模式则交换密钥参数);
    • 服务端用私钥解密得到预主密钥;
    • 双方用随机数 A+B+预主密钥推导会话密钥(对称密钥);
    • 双方发送 Finished(用会话密钥加密的握手摘要)互相确认——握手完成,之后用对称加密通信;
  3. 核心要点:

    • 握手阶段用非对称加密(RSA)或密钥协商(ECDHE)交换会话密钥(ECDHE 本身不加密业务数据,它做的是 Diffie-Hellman 协商),业务数据用对称加密(AES)——性能与安全平衡;
    • 证书由 CA 签发,客户端信任链校验——防中间人冒充;
    • TLS 1.3 简化握手(1-RTT,前向安全默认 ECDHE);
  4. 中间人攻击:没有证书校验时攻击者可冒充服务端;证书信任链是 HTTPS 防冒充的关键。

  5. 容量估算: TLS 性能:现代 CPU 上 TLS1.3 + TLS_AES_128 吞吐足够;开启 HTTP/2/3 与会话复用后握手占比很低。证书自动化(ACME)与到期告警(提前 30 天)。禁用 SSLv3/TLS1.0、弱套件。

  6. 失败与降级: 证书错误导致不可用:保留上一张有效证书应急安装路径;CDN/网关双活证书。迁移 HTTPS 时 HTTP 仅重定向,不承载业务。

【原理溯源】

  • 为什么需要“非对称换对称”两段式? 纯非对称加密每字节成本极高,扛不住业务吞吐;纯对称加密则双方无法在不安全信道上安全地共享同一把密钥。TLS 用非对称(或 ECDHE 密钥交换)在不安全信道上安全协商出对称会话密钥,之后全部业务数据走对称加密——非对称解决“密钥怎么安全地商量出来”,对称解决“数据怎么高效地保护”。

  • 证书链为什么能防冒充? 攻击者可以自己生成密钥对并声称“我是 bank.com”,但客户端会校验证书是否由系统信任的 CA 签发、域名是否匹配、是否过期/吊销。攻击者拿不到 CA 的私钥,造不出合法证书。信任链(服务器证书 → 中间 CA → 根 CA)让“信任根”可以很少,而签发可以分层。

  • 为什么现代 TLS 默认要求前向安全(PFS)? 若用 RSA 密钥交换,攻击者录下全部流量,未来某天窃取了服务器私钥,就能解开历史所有会话。ECDHE 每次握手生成临时密钥对,会话密钥不依赖服务器长期私钥——即使私钥日后泄露,历史流量仍安全。TLS 1.3 移除了纯 RSA 密钥交换,强制 PFS。

  • 为什么证书校验不能跳过(App 里很常见)? 跳过校验(信任所有证书)等于允许中间人冒充:攻击者用自签证书劫持连接,用户以为在和银行通信,实际在和攻击者通信。App 必须校验证书链与主机名,高安全场景可证书锁定(certificate pinning)。

  • HTTPS 为什么仍防不了重放与客户端侧抓包? HTTPS 保护的是网络链路。若设备已 root/越狱并安装了用户信任的代理证书,流量在客户端侧就被解密了。所以资金接口还需签名、时间戳、nonce、设备指纹(见 192 题)——HTTPS 是必要非充分条件。

  • 元理论对应: 原理 7「对抗理性与可观测的认识论」(题群见《核心考点与元理论》§7.7)

【选型判断树】

text
HTTPS/TLS 落地决策:
1. 版本与套件
   ├─ 优先 TLS 1.3(1-RTT,强制 PFS)
   └─ 兼容 TLS 1.2 + ECDHE + AES-GCM
2. 证书
   ├─ 公网 → 公共 CA(Let's Encrypt/商业 CA),自动续期
   └─ 内部 → 私有 CA / Mesh mTLS
3. App 侧
   ├─ 必须校验证书链与主机名
   └─ 高危可 pinning(注意证书轮换配合)
4. 残余风险
   └─ 根设备抓包 → 配合签名/nonce/风控(192 题)

判断口诀: 非对称换密钥,对称传数据;证书链防冒充;PFS 防未来私钥泄露。

【口述骨架】(5 分钟;框架 A,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“HTTPS=HTTP+TLS,提供加密、完整、身份认证”
0:30–2:00握手流程ClientHello→证书→验证→预主密钥→会话密钥→Finished
2:00–3:00关键思想非对称换对称;证书链防冒充
3:00–4:00现代特性TLS1.3、PFS、套件收紧
4:00–5:00收尾“HTTPS 必要但不充分,高危接口还要业务层防护”

【关键数字】

参数经验值说明
TLS 1.2 握手2-RTT可会话复用降低开销
TLS 1.3 握手1-RTT(0-RTT 复用有重放风险)0-RTT 仅用于幂等请求
证书续期90 天(Let's Encrypt)必须自动续期
推荐套件TLS_AES_256_GCM_SHA384 等禁 RC4/3DES/SHA1 签名
HSTSmax-age≥31536000强制 HTTPS,防降级

【追问链】(三层)

L1|“为什么不用纯非对称加密传数据?” → 非对称运算比对称慢几个数量级(尤其解密/签名),无法支撑高吞吐业务。TLS 只在握手时用非对称安全地交换出对称密钥,之后数据全部走 AES 等对称算法。

L2|“TLS 1.3 相比 1.2 改了什么?” → ① 握手减到 1-RTT;② 废弃不安全算法(RSA 密钥交换、CBC 套件、SHA1、RC4 等);③ 默认前向安全(ECDHE);④ 握手加密更早,减少明文暴露面。兼容性已很成熟,生产应优先。

L3|“内部服务间要不要 HTTPS?” → 建议 mTLS(双向证书),尤其跨主机房/多租户环境。同主机/受信内网可权衡性能用明文+网络策略,但零信任架构下内网也应加密认证(见 187 题)。Service Mesh 可零侵入开启。

【评分标准】

档位答案特征
60 分知道“HTTPS 是加密的 HTTP”
80 分能讲握手关键步骤;非对称换对称;证书作用
95 分PFS/前向安全;TLS1.3 改进;App 跳过校验的风险;HTTPS 局限与业务层配合

【关联题】

【自测】

  1. HTTPS 分别提供了哪三重安全属性? 参考答案:加密(防窃听)、完整性(防篡改)、身份认证(防冒充,靠证书)。
  2. 为什么业务数据不用非对称加密? 参考答案:非对称运算太慢。TLS 用非对称/ECDHE 安全协商对称密钥后,数据走对称加密。
  3. App 开发中“信任所有证书”有什么危害? 参考答案:允许中间人用自签证书冒充服务器,HTTPS 形同虚设。必须校验证书链与主机名。

195. Token 泄露/无法踢人/算法漏洞,JWT 怎么安全地用(JWT 安全) ​

【考察内容】JWT 安全是认证体系高频进阶题

【题目】你们用 JWT 做登录态,出现一串问题:Token 被 XSS 窃取后伪造请求、用户投诉“账号无法踢下线”、安全扫描发现支持 alg=none。JWT 有哪些安全坑?正确用法(短有效期、refresh、黑名单、密钥管理)是什么?

【参考答案】

  1. JWT 结构:Header(算法)+ Payload(用户信息、过期时间 exp)+ Signature(签名);特点:无状态(服务端不存),验签即可信;

  2. 安全问题:

    • 签名算法混淆:服务端必须指定算法并校验,防止攻击者把 alg 改成 none 跳过验签,或把 RS256 降级成 HS256、拿服务端公钥当 HMAC 密钥自签(经典漏洞 alg=none 与 RS256→HS256);
    • Payload 可读:只是 Base64,不能放敏感信息——只能放非敏感声明;
    • 密钥泄露:HS256 共享密钥必须强随机、妥善保管(配置中心/KMS);
    • Token 泄露:XSS 窃取(HttpOnly/短有效期缓解)、本地存储风险(放内存/HttpOnly Cookie 而非 localStorage);
    • 无法主动失效(踢人/登出):JWT 无状态=服务端无法撤销——解决:短有效期 + refresh token(可撤销)+ 必要场景维护黑名单(登出记录 jti);
  3. 最佳实践:

    • 有效期短 + refresh token 轮换;
    • 绑定用户与设备(jti 唯一标识),异常检测(异地/多端);
    • 关键操作二次验证;
    • 服务端可撤销场景(后台管理)用 Session/Redis 而非纯 JWT;
  4. 与 Session 对比:JWT 适合分布式/移动端/无状态;Session 适合需要主动管理会话的场景。

  5. 容量估算: JWT 校验是本地 CPU 操作,μs 级,可水平扩展。黑名单/踢人:短 access token(15–30min)+ refresh token + 服务端会话版本号;踢人通过版本号或 refresh 黑名单,容量按活跃用户估算(千万级 Redis 可承载)。载荷只放身份与权限版本,勿塞大对象。

  6. 失败与降级: 签名密钥泄露:立即轮换并吊销所有 token(升 session version);对旧算法 none/HS256 误用 RS 公钥当密钥的漏洞要做静态扫描。认证服务故障时可短时本地验签(在密钥未泄露前提下)。

【原理溯源】

  • JWT 为什么“验签即可信”? 服务端用密钥/私钥对 Header+Payload 签名,任何人篡改 Payload 都会导致验签失败。这省去了服务端会话存储与查询,水平扩展友好。代价是:签发之后服务端无法单方面说“这个 Token 作废了”,除非引入黑名单或短有效期。

  • alg=none 漏洞为什么会出现? 一些库实现“从 Token 的 header 读算法再验签”。攻击者把 header 改成 {"alg":"none"} 并去掉签名,若服务端按 header 执行,就跳过了验签。防御:服务端白名单指定允许的算法,绝不信任 Token 自带的 alg;HS/RS 混淆同理(用公钥当 HMAC 密钥验签)。

  • 为什么 Payload 不能放敏感信息? Payload 只是 Base64URL 编码,不是加密。任何人拿到 Token 都能解码看到内容。可以放 user_id、role、exp 等非敏感声明;手机号、身份证、权限细节应查库或放加密字段。

  • 为什么短有效期+Refresh 是主流补偿? 短有效期(15 分钟)把“Token 泄露后的可利用窗口”压小;过期后用 Refresh Token(存服务端、可撤销、可轮换)换新 Access Token。这样兼顾无状态的扩展性与可控的会话生命周期。Refresh 泄露危害更大,要更严格存储与检测。

  • 为什么高权限后台有时宁可用 Session? 管理后台需要“立即踢人”“强制下线”“按用户封锁”。Session 存在 Redis,删除 key 即失效;纯 JWT 做不到即时失效。技术选型服从于会话管理需求,不是“JWT 更先进所以处处用 JWT”。

  • 元理论对应: 原理 7「对抗理性与可观测的认识论」(题群见《核心考点与元理论》§7.7)

【选型判断树】

text
登录态选型与 JWT 加固:
1. 需要主动踢人/即时失效吗?
   ├─ 需要(后台/风控) → Session/Redis 或 JWT+黑名单
   └─ 不需要(C 端短时) → JWT 短有效期 + Refresh
2. Token 存哪?
   ├─ Web → HttpOnly+Secure+SameSite Cookie
   ├─ SPA → 内存(防 XSS)+ Refresh Cookie
   └─ 禁止 localStorage 存长期 Access Token
3. 算法
   ├─ 对称 HS256(内部)→ 密钥进 KMS
   └─ 非对称 RS256/ES256(多服务验签)→ 私钥签发,公钥分发
4. 必配
   └─ 算法白名单、exp 短、jti、敏感操作二次验证

判断口诀: 算法白名单,Payload 不放敏感,短效+Refresh,要踢人就上黑名单或 Session。

【口述骨架】(5 分钟;框架 A,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“JWT 无状态带来扩展性,也带来无法撤销的代价”
0:30–1:30结构与漏洞三段式;alg=none/算法混淆;Payload 明文
1:30–2:30泄露与存储HttpOnly vs localStorage;XSS 面
2:30–3:30失效难题短有效期+Refresh+黑名单/jti
3:30–4:30与 Session 对比按“是否要主动管理会话”选型
4:30–5:00收尾“JWT 不是银弹,失效与存储策略决定它是否安全”

【关键数字】

参数经验值说明
Access Token 有效期15–30 分钟(高危场景压到 5–15 分钟)泄露窗口小,高危取下限
Refresh Token数天~数周(常见 7–30 天),可轮换存服务端可撤销
签名算法白名单HS256 或 ES256/RS256禁 none,禁信任 Token 自带 alg
密钥长度HMAC ≥256 bit;RSA ≥2048强随机
黑名单场景登出/改密/风控封禁否则靠过期

【追问链】(三层)

L1|“refresh token 有什么用?” → Access 短效降低泄露危害;Refresh 长效且存服务端可撤销,用于静默续期。Refresh 应轮换(用旧换新),并检测重放(同一 Refresh 用两次则视为泄露,吊销家族内全部 Token)。

L2|“JWT 能放 Redis 吗?” → 放 Redis 当全量会话存储就失去了无状态意义,不如直接用 Session。合理用法是:Access 仍无状态验签,只把“已登出/已吊销的 jti”放 Redis 黑名单——有状态做减法而不是做全量。

L3|“分布式下 HS256 和 RS256 怎么选?” → HS256:签发与验签共享同一密钥,简单,但任何能验签的服务都能伪造 Token。RS256/ES256:私钥只在认证中心,各业务服务用公钥验签——伪造需要私钥,更安全。多服务架构推荐非对称。

【评分标准】

档位答案特征
60 分知道“JWT 是无状态 Token,有过期时间”
80 分讲清 alg=none、Payload 不加密、无法撤销→短效+Refresh+黑名单
95 分算法混淆与白名单;存储位置与 XSS;Refresh 轮换;与 Session 的选型边界;HS/RS 分布式选择

【关联题】

【自测】

  1. alg=none 漏洞是怎么产生的?如何防? 参考答案:服务端信任 Token 头里的算法字段,攻击者改为 none 跳过验签。防御:服务端算法白名单,绝不按 Token 自带 alg 验签。
  2. 为什么 JWT 难以实现“立即踢人”? 参考答案:无状态验签,签发后服务端不存会话。需短有效期、Refresh 吊销、或 jti 黑名单/改用 Session。
  3. Access Token 放 localStorage 有什么风险? 参考答案:XSS 可直接读取并窃走。更安全是 HttpOnly Cookie 或仅存内存。

196. 等保/个保法要求日志不能出现用户隐私(日志合规) ​

【考察内容】数据合规工程化(个保法/等保场景高频)

【题目】等保和《个人信息保护法》要求:日志和数据库查询不能明文出现手机号、身份证等个人信息。日志脱敏怎么做(打码/哈希/不落盘)?开发规范怎么定?存量日志怎么处理?

【参考答案】

  1. 识别敏感字段:手机号、身份证、银行卡、地址、token、密码——建立敏感字段清单;

  2. 日志脱敏:

    • 打日志前脱敏(工具类统一处理:手机号 138****6789、身份证 110101********1234);
    • 序列化时脱敏(Jackson 注解/自定义序列化器,统一处理对象中的敏感字段);
    • 框架层拦截(日志切面统一脱敏,避免业务漏写);
    • 禁止打完整请求参数(尤其含密码/支付信息);
    • 哈希(加盐/HMAC):只需按用户关联、不需要还原的统计与审计场景(打码用于人工核对,哈希用于关联,两者别再混);
    • 不落盘:密码、token、支付报文这类字段直接不打——比事后清洗更可靠,也是「三选一」里代价最低的一种。
  3. 日志分级:debug 可打内部字段(仅本地),生产日志必须脱敏;

  4. 数据生命周期合规:

    • 数据加密存储(见 193 题)+ 权限管控(谁能查明文);
    • 数据保留期(不能写成「日志保留 N 天自动清理」:网络日志法定下限是不少于 6 个自然月(《网络安全法》第二十三条第(三)项,2025 年修正、2026-01-01 施行;修正前这条在第二十一条)——6 个自然月最少 181 天,别按 180 天配 rotation;审计与个人信息处理记录另有 3 年(个保法第 56 条 PIA 记录)、10 年(反洗钱法第 34 条)等更长期限,等保标准只管审计记录字段与保护、不规定时长,只有无合规要求的临时 debug 日志才按天滚动,且要在策略里注明「不适用合规日志」。开发照「N 天」落地就会把网络日志清在法定期之外,题干这个合规问题等于没解决;口径见【关键数字】「日志保留期」一行);
    • 数据导出审批(运营导出用户数据需审批留痕);
    • 数据删除(用户注销后删除/匿名化);
  5. 检测:日志扫描(正则/敏感词)、脱敏规则自动化测试、审计(谁查了敏感数据);

  6. 工具:脱敏中间件/日志脱敏框架(如 logstash 脱敏 filter)。

  7. 容量估算: 日志脱敏在采集端做,避免敏感原文进磁盘/ES。脱敏规则数量与日志字段数相关,CPU 开销可接受。审计日志保留:网络日志下限出自《网络安全法》的 6 个自然月,业务/审计按监管类型自定 6 个月–3 年(等保不规定时长),按日增量×天数估存储,冷热分层。访问审计本身也要防篡改(只追加、定期哈希上链或异地备份)。

  8. 失败与降级: 发现已落盘明文:先限制日志文件权限与下载,再安全清理/轮转;排查下游 ES/HDFS/数仓是否有副本。合规事件按制度上报,不能只删文件了事。

【原理溯源】

  • 为什么日志是泄露重灾区? 日志会流向 ELK、对象存储、第三方 APM、开发本地、工单截图——传播面远大于数据库。一次“方便排查问题打了完整请求体”就可能让手机号/身份证进入不可控的系统。合规视角:日志里的个人信息与库里的同等对待。

  • 为什么要在框架层统一脱敏而不是靠开发自觉? 人总会忘。把脱敏做进日志框架(切面/序列化器/Logback 插件),敏感字段默认脱敏,开发想打全号反而要显式绕过并被审计——把安全从“个人自觉”变成“系统默认”。

  • 打码与哈希分别适合什么场景? 打码(135****6789)适合展示与排障(可核对前后位);哈希(加盐)适合需要“同一用户可关联但不可还原”的统计/审计场景。密码类永不落日志;token 类打码或不落盘。

  • 为什么要有保留期与删除权? 个保法要求目的明确、最短必要保存。日志永久堆积既是存储成本也是合规风险。用户注销后,其个人信息应从日志/数仓中删除或匿名化——这是“被遗忘权”的工程落地,依赖敏感字段清单做检索。

  • 如何证明“我们已脱敏”? 需要检测闭环:自动化扫描生产日志样本中的手机号/身份证正则;脱敏规则的单元测试;访问审计(谁导出了用户数据)。没有检测的规范只是纸面合规。

  • 元理论对应: 原理 7「对抗理性与可观测的认识论」(题群见《核心考点与元理论》§7.7)

【选型判断树】

text
日志合规怎么落地?
1. 建清单
   └─ 手机号/身份证/银行卡/地址/token/密码/设备号
2. 脱敏层选点
   ├─ 业务代码 → 工具类(易漏,不推荐单独依赖)
   ├─ 序列化框架 → Jackson 注解(推荐)
   └─ 日志框架 → Logback/Log4j2 插件(默认兜底)
3. 展示与存储
   ├─ 库内 → 加密(193 题)
   ├─ 日志 → 打码/哈希/不落盘
   └─ 导出 → 审批+留痕
4. 生命周期
   └─ 保留期 → 按类别分档清理:网络日志法定不少于 6 个自然月(最少 181 天,别按 180 天配)不得按天删、业务/审计按等保定级(6 个月–3 年)、仅临时 debug 按天滚动;到期归档或销毁;注销 → 删除/匿名化
5. 检测
   └─ 正则扫描 + 规则测试 + 访问审计

判断口诀: 先清单,再框架默认脱敏,存储加密,导出审批,扫描验证。

【口述骨架】(5 分钟;框架 A,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“日志泄露面比库更大,个保法同等看待”
0:30–1:30脱敏手段打码/哈希/不落盘;框架层统一处理
1:30–2:30工程化序列化注解+日志切面;禁打完整参数
2:30–3:30生命周期保留期、导出审批、注销删除
3:30–4:30检测正则扫描、规则测试、访问审计
4:30–5:00收尾“默认脱敏+可验证,才算合规落地”

【关键数字】

参数经验值说明
手机号打码前3后4138****6789
身份证打码前6后4保留地址码与校验位线索
日志保留期网络日志法定下限不少于 6 个自然月(网安法第二十三条三);按数据类型另有 3 年(个保法 56 条、网络数据安全管理条例)、10 年(反洗钱法 34 条)等更长期限——等保不规定留存时长到期归档或销毁;6 个自然月最少 181 天,别按 180 天折算,也别写"3 天"这类低于下限的默认值
生产日志级别info/warn,禁 debug 全参debug 仅本地
导出审批100% 留痕含操作人/范围/目的

【追问链】(三层)

L1|“日志里能存加密后的字段吗?” → 可以。密文本身不是明文个人信息,可用于关联排查。但解密权限必须管控,且算法/密钥管理要合规(KMS)。注意:确定性加密可能泄露相等关系,按场景选择。

L2|“排查线上问题必须要全号怎么办?” → ① 用脱敏值+内部工具在授权下临时解密;② 走审批的临时明文查询通道,全量审计;③ 用用户 ID/订单号作为主键关联,避免在日志里直接用手机号。原则:排查便利不能突破最小必要。

L3|“存量历史日志已经打了全号怎么办?” → ① 摸底范围(哪些系统、时间窗);② 可回刷的做脱敏重写或删除;③ 不可改写的归档隔离+访问收紧;④ 评估是否达到告知/报告门槛;⑤ 新日志立即上框架脱敏,防止增量扩大。

【评分标准】

档位答案特征
60 分知道“日志要打码脱敏”
80 分敏感清单、框架层统一脱敏、生命周期与导出审批
95 分论证日志传播面;默认安全工程化;检测闭环;存量治理与个保法义务

【关联题】

【自测】

  1. 为什么日志脱敏要在框架层做而不是业务代码里? 参考答案:业务代码靠自觉会漏;框架/序列化层默认脱敏把安全变成系统默认行为,降低遗漏概率。
  2. 手机号在日志中打码和哈希分别适合什么场景? 参考答案:打码适合展示排障(可核对);哈希适合需要用户关联但不可还原的统计审计。
  3. 用户行使删除权时,日志侧要做什么? 参考答案:按敏感字段检索并删除或匿名化相关日志/数仓记录,并保留处理审计证明。

197. 用户改个订单号就能看别人的订单(越权漏洞) ​

【考察内容】越权是安全测试最高频考点(甲方安全/全栈高频)

【题目】安全测试发现:用户 A 把请求里的订单号改成别人的,就能查看/修改他人订单(水平越权);普通用户直接调管理员接口也能成功(垂直越权)。两类越权怎么防?从接口设计、鉴权、数据权限几个层面给出方案。

【参考答案】

  1. 两类越权:

    • 水平越权:同角色用户访问他人资源(改 order_id 查别人的订单);
    • 垂直越权:低权限用户执行高权限操作(普通用户调管理接口);
  2. 防护:

    • 垂直越权(权限校验):RBAC 权限模型 + 后端接口鉴权(注解/拦截器校验角色权限,不能只靠前端隐藏按钮);接口级权限矩阵(谁可调哪个接口);
    • 水平越权(资源归属校验):接口内必须校验资源归属——查询/更新前检查资源 owner(order.user_id == 当前用户)或数据权限规则(SQL 注入 owner 条件,如 WHERE user_id = 当前用户);
    • 防遍历:对外单号要不可枚举——裸雪花的高 41 位是时间戳、低位是机器 ID 与序列号,相邻号可预测,因此可反推发号趋势与单量,须加随机位、加密编码或让对外号与内部主键分离(见第 121 题);随机 UUID/随机 ID 才谈得上「不可预测」、敏感接口频控;
  3. 架构级:统一鉴权中间件(网关/框架层做权限校验,业务层做资源归属校验)、数据权限框架(MyBatis 拦截器自动拼 owner 条件);

  4. 测试:越权测试用例(A 访问 B 的资源)纳入安全测试;安全扫描(IDOR 检测);

  5. 原则:永远不信任前端传入的 ID,后端必须二次确认归属与权限。

  6. 容量估算: 越权防护是代码正确性问题不是容量问题;但网关鉴权 QPS、权限中心查询要缓存。经验:每个资源接口必须做“数据归属校验”,不能只做菜单权限。自动化测试:改 ID 横向越权用例应进 CI。渗透测试频率:核心金融接口至少年检+重大变更后。

  7. 失败与降级: 发现越权:立即网关按用户维度限流/下线接口,吊销可疑会话,排查访问日志评估泄露范围。短时可在网关加“资源属主校验”补丁策略(若网关能拿到用户与资源关系)。

【原理溯源】

  • 为什么两类越权要分开防? 垂直越权缺的是“这个接口需要什么角色”;水平越权缺的是“这个资源归谁”。前者是接口级权限矩阵,后者是数据级归属校验。只做 RBAC 只能防垂直,普通用户之间互查订单照样失守——很多团队误以为“有权限系统就安全了”。

  • 为什么前端隐藏按钮没用? 接口是公开的攻击面。攻击者可直接 curl/改包调用任何已知 URL。权限必须在服务端校验;前端隐藏只是体验,不是安全。

  • 为什么 SQL 层拼 owner 条件是好模式? 把 user_id = 当前用户 做成数据权限框架(MyBatis 拦截器自动拼接),业务代码难以忘记。即使开发写了 selectById(id),框架也会自动限制数据范围。这比“每个接口手写 if owner”更不易漏。

  • 为什么 ID 可预测会放大越权? 自增 ID 让攻击者可以遍历(10001、10002...)批量拉取他人数据。雪花/随机 ID 增加遍历成本,但不能替代归属校验——只是降低自动化攻击效率。纵深上归属校验才是根治。

  • 为什么网关鉴权不够? 网关适合认证(你是谁)与粗粒度接口权限(能不能调 /admin);资源归属(这个 order 是不是你的)只有业务层知道。安全责任必须分层:网关认身份,业务认归属。

  • 元理论对应: 原理 7「对抗理性与可观测的认识论」(题群见《核心考点与元理论》§7.7)

【选型判断树】

text
越权防护分层:
1. 垂直越权
   ├─ 网关/拦截器:接口级 RBAC/ABAC
   └─ 前端隐藏只是体验
2. 水平越权
   ├─ 查询条件强制加 owner(数据权限框架)
   └─ 业务代码二次校验 resource.userId == currentUser
3. 防遍历
   └─ 随机 ID/加随机位的雪花 + 频控(裸雪花相邻号可预测,不能直接当防遍历)
4. 验证
   └─ 越权用例进安全测试(A 访 B 的资源)

判断口诀: 垂直管接口权限,水平管资源归属;不信任前端 ID,SQL 自动拼 owner。

【口述骨架】(5 分钟;框架 D,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“越权分水平(资源归属)与垂直(角色权限)两类”
0:30–1:30垂直防御RBAC + 后端接口鉴权;前端隐藏无效
1:30–2:30水平防御owner 校验;数据权限框架拼条件
2:30–3:30防遍历不可预测 ID + 频控
3:30–4:30工程化网关认身份、业务认归属;安全测试
4:30–5:00收尾“每个带 ID 的接口都要问:这个资源归谁”

【关键数字】

参数经验值说明
ID 类型雪花/UUID/随机串避免纯自增暴露量级
鉴权位置100% 后端前端仅体验
数据权限MyBatis 拦截器自动拼减少业务遗漏
安全测试每个资源接口要有 A→B 用例纳入回归
网关 vs 业务网关=身份/粗权限;业务=归属分层

【追问链】(三层)

L1|“如何自动化发现越权?” → ① 安全测试平台准备两个账号,用 A 的 Token 请求 B 的资源 ID,比对是否 200;② 代码审计扫描“取 id 后未校验 owner”的模式;③ 渗透测试 IDOR。应纳入 CI/定期扫描。

L2|“管理员接口和用户接口在同一个服务里,怎么隔离?” → 路径前缀隔离(/admin/**)+ 独立鉴权拦截器 + 独立部署更佳(管理面与用户面分离)。至少保证 /admin 强制高权限角色,且默认拒绝。

L3|“数据权限框架会不会误伤跨部门协作查询?” → 需要显式的“数据范围”模型:本人/本部门/全部,角色配置数据范围而不是写死。客服“查用户订单”应走受控工具(全量权限+操作审计),而不是给业务接口开后门。

【评分标准】

档位答案特征
60 分知道“要校验权限、不能改别人 ID”
80 分区分水平/垂直;后端鉴权;owner 校验;防遍历
95 分数据权限框架工程化;网关/业务分层责任;自动化测试;客服等特殊场景受控例外

【关联题】

【自测】

  1. 水平越权和垂直越权的区别是什么? 参考答案:水平是同角色用户访问他人资源;垂直是低权限调高权限接口。防护分别是资源归属校验与接口角色校验。
  2. 为什么前端隐藏管理按钮不能防垂直越权? 参考答案:攻击者可直接构造 HTTP 请求调用接口。权限必须在服务端校验。
  3. 怎么从架构上减少“忘记校验 owner”? 参考答案:数据权限框架在 SQL 层自动拼接 user_id/数据范围条件;代码审查与越权测试用例兜底。

198. 服务上 K8s 后频繁被杀、启动慢、流量异常(K8s 部署适配) ​

【考察内容】云原生部署是 2024+ 高频题

【题目】服务容器化上 K8s 后问题不断:滚动发布时流量切到还在启动的实例、停机时正在处理的请求被断、Pod 内存超限被杀。开发侧要注意什么(优雅停机、就绪/存活探针、资源限制、日志输出)?

【参考答案】

  1. 优雅停机(关键):

    • 应用注册 PreStop Hook + SIGTERM 处理:收到停止信号后,先摘除注册中心/负载均衡流量(下线),停止接收新请求,处理完在途请求(等待时间窗口)再退出——避免请求中断/消息丢失;
    • 关闭线程池/连接池/消息消费前先排空;
  2. 探针配置:

    • readinessProbe(就绪:可以接流量了)——应用启动完成、依赖就绪才返回 200;
    • livenessProbe(存活:进程活着)——死锁/卡死时 K8s 重启;
    • startupProbe(启动慢的应用);
    • 探针路径要做成轻量接口(不能依赖下游,否则误判);
  3. 资源管理:requests/limits 合理设置(CPU/内存),防止 OOM Kill(JVM 容器内存要 -XX:MaxRAMPercentage 感知容器限制);避免进程内线程数=CPU 核数×N 的假设(容器核数感知);

  4. 无状态与存储:日志输出到 stdout(由平台采集,不写本地文件);有状态数据用 PVC/外部存储;

  5. 配置:环境配置通过 ConfigMap/环境变量注入,不打包进镜像;

  6. 其他:镜像瘦身(启动快)、健康检查、优雅退出超时设置(terminationGracePeriodSeconds)。

  7. 容量估算: K8s 资源:requests/limits 按压测单实例峰值 CPU/内存设置,CPU limit 过低会导致 throttle(表现为慢但不是 OOMKilled)。HPA:按 CPU 或自定义 QPS 指标,扩容冷却与稳定窗口要配。就绪探针:避免未预热实例接流。驱逐:PDB 保证最小可用副本数。镜像体积影响启动秒数。

  8. 失败与降级: 频繁被杀先看 OOMKilled vs Evicted vs liveness 失败,处置不同。紧急:放宽 resources/探针、增加副本、临时固定节点。应用要支持优雅退出(停接流→处理完在途→退出),减少滚动发布失败。

【原理溯源】

  • 为什么 K8s 默认 kill 会造成请求中断? 滚动更新时 K8s 先删旧 Pod(发 SIGTERM,宽限期后 SIGKILL)。若应用不处理 SIGTERM,或摘流量太慢,在途请求与新请求会打到正在死亡的实例。正确顺序:摘流量 → 排空在途 → 退出。PreStop 里 sleep 几秒可让 endpoints 更新传播到 kube-proxy。
  • readiness 与 liveness 为什么必须分开? readiness 控制“是否接流量”,失败只是不接新流量,不影响存活;liveness 控制“是否重启”,失败会杀容器。若把“下游 Redis 超时”设成 liveness,会引发级联重启风暴(下游一抖,全集群重启)。liveness 只探活进程本身(死锁/卡死)。
  • 为什么 JVM 在容器里容易 OOM? 传统 JVM 按宿主机内存算堆,容器 limits 只给 2G 时 JVM 可能按 32G 规划堆,直接被 cgroup OOM Kill。Java 8u191/10 起 UseContainerSupport 就是默认开启(OpenJDK 17 实测 UseContainerSupport = true {default}、MaxRAMPercentage = 25.0 {default}),要显式关掉才回到「按宿主机算」;生产仍建议显式设 MaxRAMPercentage(如 70%),别吃 25% 的默认值。还要预留元空间、栈、堆外内存。
  • 为什么日志要打 stdout? 容器文件系统是临时的,Pod 重建即丢;且多副本写本地文件无法集中检索。stdout/stderr 由节点日志驱动收集,进入统一日志平台(与第 140 题一致)。有状态数据一律外置(对象存储/PVC/DB)。
  • 为什么镜像要瘦、启动要快? 滚动发布与弹性扩缩容都依赖“快速就绪”。启动 3 分钟则:发布慢、扩容接不住流量峰值、故障恢复时间长。优化:分层构建、小基础镜像、懒加载非核心、减少启动时全量预热(或用 startupProbe 容忍慢启动)。

【选型判断树】

text
K8s 上的服务适配清单:
1. 流量切换
   └─ readinessProbe 轻量自检;PreStop 摘流量+sleep
2. 进程死亡
   └─ SIGTERM → 排空线程池/在途请求 → 退出;terminationGracePeriod 足够
3. 卡死重启
   └─ livenessProbe 只探本进程,不依赖下游
4. 慢启动
   └─ startupProbe + 合理 initialDelay
5. 内存
   └─ limits 设置 + JVM MaxRAMPercentage;预留堆外
6. 可观测
   └─ 日志 stdout;指标 /metrics;配置 ConfigMap

判断口诀: readiness 管接流量,liveness 管重启;SIGTERM 先摘流量再排空;JVM 认容器内存。

【口述骨架】(5 分钟;框架 D,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“容器化问题集中在生命周期:启动、接流量、停机、内存”
0:30–1:30探针readiness/ liveness/ startup 分工;探针不依赖下游
1:30–2:30优雅停机SIGTERM→摘流量→排空→退出;PreStop 作用
2:30–3:30资源与 JVMlimits;MaxRAMPercentage;堆外内存
3:30–4:30日志与配置stdout;ConfigMap;镜像瘦身
4:30–5:00收尾“把 K8s 生命周期钩子接对,比改业务代码更重要”

【关键数字】

参数经验值说明
terminationGracePeriodSeconds30–60s大于排空所需
PreStop sleep5–10s等 endpoint 摘除传播
JVM MaxRAMPercentage50–75%预留堆外/元空间
readiness启动完成+本地依赖不调下游
liveness轻量自检,failureThreshold 放宽防误杀
日志stdout/stderr由平台采集

【追问链】(三层)

L1|“为什么容器里 JVM 会 OOM?” → 分版本:≤8u181/9 才是默认按宿主机内存规划堆(容器 limits 小时被 cgroup 杀);8u191/10 起 UseContainerSupport 默认开启,堆已按容器 limit 算(本机 OpenJDK 17 实测默认 UseContainerSupport=true、MaxRAMPercentage=25.0)。所以现代 JDK 还被杀,通常是堆加元空间、线程栈、直接内存、code cache 的总量超过 limit,不是堆算错了——要显式调 MaxRAMPercentage 并给堆外留余量。

L2|“探针依赖了 Redis,Redis 抖动导致全集群重启怎么办?” → 这是 liveness 误用。liveness 只探进程;依赖检查放 readiness(暂时不接流量)或启动探针。修复后应提高 liveness 的失败阈值与延迟,避免瞬时抖动触发重启。

L3|“发布时仍有少量 502,怎么继续压?” → ① PreStop 充分 sleep;② 优雅退出排空时间加长;③ 网关/Service 的连接排空(LB 摘除延迟);④ 应用侧处理 in-flight 请求完成后再关 HTTP server;⑤ 发布策略改 maxUnavailable=0/maxSurge>0 先扩后缩。

【评分标准】

档位答案特征
60 分知道“要配健康检查、限制资源”
80 分讲清三类探针分工、优雅停机流程、JVM 容器感知
95 分liveness 级联重启风险;PreStop 与 endpoint 传播;日志 stdout;先扩后缩发布策略

【关联题】

【自测】

  1. readinessProbe 和 livenessProbe 有什么区别? 参考答案:readiness 失败则不接流量;liveness 失败则重启容器。依赖下游的检查只能放 readiness,防级联重启。
  2. 滚动发布时如何避免请求打到正在退出的实例? 参考答案:SIGTERM/PreStop 先摘注册与 Service 流量,sleep 等传播,排空在途请求再退出。
  3. 为什么 JVM 需要 MaxRAMPercentage? 参考答案:让堆大小基于容器 limits 而非宿主机内存,避免超限被 OOM Kill。

199. 一次改动跨 5 个服务,怎么只灰度给 1% 用户(全链路灰度) ​

【考察内容】全链路灰度是微服务高级工程题(大厂架构岗高频)

【题目】一次版本升级同时改了 A、B、C 三个服务,如果只灰度 A,流量到旧 B 就错乱了。怎么做全链路灰度(按用户维度染色,灰度流量在整个调用链都走新版本)?染色标记怎么传递?

【参考答案】

  1. 需求:多服务联动变更,全量发布风险大——需要按用户维度的灰度(同一用户全程走新版本,避免 A 新版+B 旧版混用);

  2. 方案:

    • 流量染色/标签路由:网关/入口按用户 ID 或请求头打标(灰度用户标记,如用户 ID hash 前 1%~5% 或白名单),通过标签路由(Nacos/Dubbo/网关)把灰度用户的请求路由到新版本服务,非灰度用户走旧版本——链路内所有服务按同一标签路由(全链路灰度);
    • 注册中心支持(Nacos 多版本/灰度分组);K8s 可用 Istio 流量管理(destination rule 按 header 路由);
  3. 流程:确定灰度比例(起点按爆炸半径取 1%~5%,其后 5%→20%→50%→100%,与【关键数字】的阶梯同一套)→ 观察灰度用户的关键指标(错误率/RT/转化/核心业务数据)→ 每个阶段卡点 → 全量后清理灰度标记;

  4. 关键:

    • 数据兼容:新版本可能写新字段——旧版本要能兼容读(前向兼容,DB 变更先行灰度);
    • 灰度用户画像要具代表性(不能只灰度内部员工);
    • 回滚:灰度失败则把标签路由切回旧版本(秒级);
  5. 对比部署灰度(按实例):部署灰度只管“新老实例共存”,全链路灰度管“同一用户全链路一致版本”,两者常结合使用。

  6. 容量估算: 全链路灰度:标签从网关透传到 RPC/MQ/缓存键或影子表。流量放大:跨服务后实际打到下游的灰度比例会衰减/错乱,要保证染色一致。观察指标:核心业务+错误率+性能,样本量按统计功效估算(转化类指标要按「转化事件数」而不是会话数算,且必须与放量节奏对齐:日会话 720 万时,1% 档 60 分钟只有 720万×1%÷1440×60≈3000 会话,按 3% 转化率折算仅约 90 个转化事件/组,离「数千样本/组」差一两个数量级——所以转化率卡点只能放到 20%~50% 档、窗口按小时~天;日流量再小一档就要放到天级;1%~5% 档的卡点只写工程护栏)。跨 5 服务时,任一环节丢染色都会导致“灰度不纯”。

  7. 失败与降级: 灰度出问题:一键把染色流量切回基线;熔断灰度服务实例。灰度数据要「可识别+可回滚」:写入带灰度标记位或独立字段、schema 只加可空列(前向兼容),出问题按标记清理。但别把「影子库」当灰度手段:本块【原理溯源】「DB 变更必须先行」的前提就是灰度期新老实例同时写真实主表,灰度流量若落影子库就没验证过真实数据路径,还要额外回迁;影子表/影子库是压测的手段,两者不要混用。

【原理溯源】

  • 为什么按实例灰度不够? 实例灰度下,用户第一次请求可能落到 A 新版,第二次落到 A 旧版;且 A 新版调用 B 时可能落到 B 旧版。若新版改了协议/数据语义,会出现“时好时坏”的灵异 Bug,极难排查。全链路灰度保证同一用户的调用路径上版本一致。
  • 染色标记为什么要全链路透传? 标记只在入口打还不够——服务 A 调 B、B 发 MQ 给 C,标记必须跟着走。手段:RPC 框架隐式透传(attachment/header)、MQ 消息头、线程上下文(注意线程池传递)、HTTP Header。丢失一环就会“染色断链”。
  • 为什么 DB 变更必须先行? 灰度期间新老实例同时写库。若新版写新列,旧版读不懂;若新版改枚举语义,旧版解析错误。正确顺序:先做兼容的 DB 变更(加可空列)→ 发布兼容代码 → 灰度新逻辑 → 全量后再清理。代码可以灰度,不兼容的 schema 变更不能。
  • 为什么灰度用户要有代表性? 只灰度员工/白名单,流量模式(机型、网络、操作路径)与真实用户不同,指标失真。应按 user_id hash 稳定采样,保证画像覆盖;对极高危功能可叠加白名单。
  • 为什么回滚能做到秒级? 标签路由是控制面配置,改配置即把灰度流量切回旧版本,无需重新部署。前提是旧版本仍在线且能处理灰度期间产生的新数据——又回到数据兼容问题。

【选型判断树】

text
多服务联动怎么灰度?
1. 变更是否跨服务且互依赖?
   ├─ 否 → 普通部署灰度即可
   └─ 是 → 全链路染色
2. 染色维度
   ├─ 用户体验一致性优先 → user_id hash 稳定采样
   └─ 极高危功能 → 白名单内测再放量
3. 实现
   ├─ 微服务框架 → Nacos/Dubbo 标签路由 + 透传
   └─ K8s → Istio VirtualService/DestinationRule
4. 前置条件
   └─ DB/接口向后兼容;回滚路径可用
5. 观察指标
   └─ 错误率、RT、资源(1%~5% 档);核心转化只在 20% 以上档位作为卡点

判断口诀: 先染色再透传,DB 先行保兼容,按比例放量看指标,失败秒级切回。

【口述骨架】(5 分钟;框架 A,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“跨服务联动变更要按用户全链路灰度,避免新老混用”
0:30–1:30染色与路由入口打标;标签路由到新版本组
1:30–2:30标记透传RPC header/MQ/上下文;断链问题
2:30–3:30数据兼容DB 先行;加列不删列
3:30–4:30放量与回滚比例阶梯;指标卡点;秒级切回
4:30–5:00收尾“染色保证路径一致,兼容保证可回滚”

【关键数字】

参数经验值说明
灰度比例阶梯1%→5%→20%→50%→100%窗口按指标类型分两档:错误率/RT/分布这类工程护栏每档 15–60 分钟即可;转化/GMV 这类业务显著性卡点要 20%~50% 档+小时~天级窗口(样本数按转化事件算)
白名单阶段可先 100% 内部员工再转 hash 采样
染色 keyuser_id / uid hash稳定,同一用户始终同版本
回滚改路由配置,秒级依赖旧版本仍在线
DB 变更提前 1 个发布周期加可空列

【追问链】(三层)

L1|“标签怎么透传到 MQ 下游?” → 消息体外加 headers(Kafka headers / RocketMQ properties)写入染色标记;消费者取出后放入 ThreadLocal/Context,再在后续 RPC 中透传。注意线程池、异步回调场景的 Context 传递。

L2|“灰度期间数据不一致怎么查?” → 日志与链路追踪必须带灰度标记(trace 中记录 version/route tag),出问题可按“是否灰度流量”切片对比。监控大盘也应分灰度/基线两条曲线。

L3|“全量后如何安全清理旧版本?” → ① 灰度标记流量归零;② 观察旧版本无流量后下线;③ 下个迭代清理兼容代码与废弃列;④ 保留回滚窗口一段时间。避免“全量即删旧”导致无法回滚。

【评分标准】

档位答案特征
60 分知道“按用户灰度、打标记”
80 分讲清标签路由、全链路透传、数据兼容、指标卡点
95 分染色断链场景;DB 先行时序;代表性采样;与部署灰度结合;清理节奏

【关联题】

【自测】

  1. 为什么只灰度 A 服务不够? 参考答案:A 新版会调到 B 旧版,协议/语义不兼容时行为错乱。需要同一用户全链路都走新版本。
  2. 染色标记丢失一环会怎样? 参考答案:染色断链,后续服务可能路由到旧版本,出现新老混用。RPC/MQ/异步上下文都要透传。
  3. 全链路灰度对数据库变更的要求是什么? 参考答案:必须向后兼容并先行发布(如加可空列),保证新老实例共存期都能读写。

200. 接口字段要变,老客户端不能挂(接口兼容性) ​

【考察内容】接口演进与兼容性工程

【题目】线上接口需要改字段(加必填参数/改返回结构/改逻辑),但老版本 App 还在大量使用。怎么做接口版本管理(URL 版本/Header 版本)?怎么保证向后兼容?新老逻辑怎么共存与下线?

【参考答案】

  1. 原则:接口向后兼容(老调用方不感知变更),变更不兼容时必须版本化;

  2. 版本策略:

    • URL 版本:/v1/orders、/v2/orders——显式、最常用;新版本独立实现,老版本保留;
    • Header/参数版本:Accept 头或 version 参数(同 URL 多版本)——RESTful 风格;
    • 兼容性判断:
      • 新增字段:兼容(老客户端忽略未知字段——序列化要忽略未知属性);
      • 删字段/改类型/改语义/改必填:不兼容,必须新版本;
    • 字段默认值兜底:新逻辑缺老字段时给默认值;
  3. 演进策略:

    • 加字段先于改逻辑:DB 加列(可空/默认值)→ 发布支持新字段的版本 → 老版本共存;
    • 版本共存期:老版本维护(bug 修复),约定下线周期(如保留 6 个月/两个大版本);
    • 服务端适配层:一个接口兼容多版本入参(参数转换器);
    • 客户端强制升级策略(版本号+最低支持版本,老版本接口提示升级);
  4. 实践:契约测试(消费者驱动,防接口破坏)、接口变更评审、文档(OpenAPI)同步、兼容性自动化测试(老版本用例持续回归)。

  5. 容量估算: 接口兼容成本:老客户端版本长尾可能数月–1年+,必须按活跃版本分布决定兼容窗口。字段变更原则:只增不删不改语义;删除字段先停写再停读。契约测试与流量录制回放。网关层可做字段适配,但会积累技术债。

  6. 失败与降级: 老客户端大面积报错:网关按版本兜底转换或返回兼容响应;必要时服务端保留旧逻辑开关。发布后监控按客户端版本拆分错误率。

【原理溯源】

  • 为什么“新增字段”通常兼容而“删改字段”不兼容? 多数序列化协议/JSON 解析器在关掉「未知字段报错」后会忽略未知字段(Protobuf 默认忽略;裸 Jackson 默认抛错,是 Spring 的 Jackson2ObjectMapperBuilder 才关掉)——老客户端遇到新字段可安全跳过。但删字段会让老客户端解析失败或空指针;改类型导致反序列化异常;改语义更隐蔽(同字段含义变了,客户端逻辑错误)。所以向前兼容容易,向后兼容难。
  • 为什么 App 接口必须假设“老客户端永远存在”? App 发布后用户不一定升级。长尾版本可能存活数年。服务端必须同时服务多版本客户端,或用“最低支持版本”强制淘汰。这是移动端接口与内部 RPC 最大的不同——客户端集合不可控。
  • 为什么 URL 版本更常用? 显式、可路由、可按版本独立限流与监控、缓存键清晰。Header 版本更“REST 纯粹”,但网关路由与缓存配置更绕。内部 API 可用 Header,对外 App 接口 URL 版本更直观。
  • 为什么契约测试重要? 消费者驱动的契约测试(Pact 等)让提供者在 CI 阶段就知道“我这个改动会不会打破消费者期望”。没有契约时,兼容性靠人眼 review 与联调,容易漏。接口是团队间的社会契约,要像代码一样测试。
  • 下线老版本的正确姿势? 不能“看着没人用了就删”。要:① 统计版本流量直至 0;② 通过 App 弹窗/接口报错强制升级;③ 提前公告;④ 保留一个发布周期的双跑;⑤ 真正删除。很多事故来自“以为没人用了”。

【选型判断树】

text
接口怎么改?
1. 变更类型
   ├─ 加可选字段/加接口 → 向后兼容,可直接上
   ├─ 加必填字段 → 新版本或默认值兼容
   └─ 删字段/改类型/改语义 → 必须新版本
2. 版本化方式
   ├─ 对外 App → URL /v1 /v2
   └─ 内部/REST 纯粹 → Header/参数
3. 共存
   └─ 适配层/参数转换;老版本修 bug 不删
4. 下线
   └─ 流量监控→强制升级→公告→双跑→删除
5. 防回归
   └─ 契约测试 + 老版本用例进 CI

判断口诀: 加字段可兼容,删改要版本化;先改 DB 再改代码;下线看流量不看感觉。

【口述骨架】(5 分钟;框架 D,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“移动端接口要假设老客户端永远存在”
0:30–1:30兼容性判断加字段 OK;删改类型语义不 OK
1:30–2:30版本化URL vs Header;新旧共存
2:30–3:30演进时序DB 加列→代码双支持→灰度→清理
3:30–4:30下线流量归零+强制升级+公告
4:30–5:00收尾“契约测试把兼容性变成门禁”

【关键数字】

参数经验值说明
版本共存期≥6 个月或 2 个大版本以流量为准
最低支持版本按活跃用户分布定强制升级阈值
序列化忽略未知字段Protobuf 默认忽略;Jackson 原生默认抛错,是 Spring 的 Jackson2ObjectMapperBuilder 才把它关掉
契约测试消费者驱动,CI 必跑防破坏性变更
文档OpenAPI 同步变更评审留痕

【追问链】(三层)

L1|“新版本上线老版本什么时候删?” → 以调用量监控为准:版本流量降到 0(或业务可接受的阈值)且过了强制升级窗口,再删除。不能按“发版日期+拍脑袋”。删除前保留双跑一个观察期。

L2|“字段类型改不了怎么办?” → 新版本用新字段名(如 amount 改 amount_cent 为整型分),老字段保留过渡,数据双写,客户端迁完再删旧字段。禁止原地改类型。

L3|“如何防止同事无意中改坏接口?” → ① 变更评审(接口治理委员会/架构师);② 契约测试进 CI;③ 消费者用例回归;④ 破坏性变更检查工具(与 OpenAPI diff)。把“人盯”升级为“机器拦”。

【评分标准】

档位答案特征
60 分知道“要兼容老版本,可以加字段”
80 分讲清兼容性判断、URL/Header 版本、共存与下线
95 分移动端长尾;DB 先行时序;契约测试;类型迁移用新字段名;下线看流量

【关联题】

【自测】

  1. 为什么新增字段通常是兼容的? 参考答案:多数客户端序列化会忽略未知字段(前提:解析器没开「未知字段报错」——裸 Jackson 默认会抛错)。删除/改类型/改语义才会破坏老客户端。
  2. 对外 App 接口推荐哪种版本化方式? 参考答案:URL 版本(/v1 /v2),显式、易路由、易监控。内部可用 Header 版本。
  3. 老版本接口如何安全下线? 参考答案:监控该版本调用量归零 → 强制升级策略 → 用户公告 → 双跑观察 → 再删除。

后端主库第 1–208 题 + 国企金融专题第 209–228 题。


持续学习,持续积累。