post: E2E 是个伪需求(配置中心方案C推翻记)
This commit is contained in:
@@ -0,0 +1,97 @@
|
|||||||
|
---
|
||||||
|
title: "E2E 是个伪需求"
|
||||||
|
description: "配置中心的端到端加密方案,上午落地,下午死在了自己的安全评估里。不问威胁模型就堆加密原语,堆得越专业越危险。"
|
||||||
|
date: 2026-08-24T20:25:00+08:00
|
||||||
|
draft: false
|
||||||
|
tags:
|
||||||
|
- 架构
|
||||||
|
- 安全
|
||||||
|
- 配置中心
|
||||||
|
---
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
项目里散落着六处配置:客户端直连的对象存储密钥、推送 Token、第三方 API Key……全部硬编码在客户端代码里。今天要把它们收进统一配置中心,核心诉求一句话:
|
||||||
|
|
||||||
|
> 这些核心配置不能被泄露,不能在被人逆向时直接拿到。
|
||||||
|
|
||||||
|
围绕这句话,方案迭代了三轮,最终回到了一个最初根本没考虑过的答案。
|
||||||
|
|
||||||
|
## 方案 C:看起来很专业
|
||||||
|
|
||||||
|
上午拍板的是一套「按凭据类型区分加密」的设计:
|
||||||
|
|
||||||
|
| 凭据类型 | 前缀 | 存储 | 服务端回显 |
|
||||||
|
|---------|------|------|-----------|
|
||||||
|
| 纯客户端凭据 | `cred_` | **客户端公钥混合加密**(RSA-OAEP + AES) | ❌ 服务端无私钥,不可见 |
|
||||||
|
| 服务端可管理 | `scred_` | 对称加密 | ✅ 可回显编辑 |
|
||||||
|
|
||||||
|
听起来很美:客户端上传公钥,配置中心用公钥加密,数据库里全是密文,连服务端管理员都看不到明文--教科书级的端到端加密(E2E)。落地、验证、全绿。
|
||||||
|
|
||||||
|
下午做安全评估的时候,它塌了。
|
||||||
|
|
||||||
|
## 评估:三个致命缺陷
|
||||||
|
|
||||||
|
### 1. 公钥踩踏
|
||||||
|
|
||||||
|
一份配置要下发给多个客户端,加密时用**谁的**公钥?A 的公钥加密,B 就解不开;给每人存一份密文,配置更新就要全量重加密。E2E 假设「一对一私聊」,而配置中心是「一对多广播」--场景就不匹配。
|
||||||
|
|
||||||
|
### 2. 公钥冒领
|
||||||
|
|
||||||
|
公钥是**公开的**。任何人都能拿到公钥,加密一份假凭据提交上来,服务端无私钥无法验证内容真伪,只会原样存储、原样下发。你防住了自己人看,没防住别人写。
|
||||||
|
|
||||||
|
### 3. 两端都有明文,E2E 防的是谁?
|
||||||
|
|
||||||
|
这是最根本的一问。E2E 的威胁模型是「**服务端不可信**」(即时通讯怕服务器运营商偷看)。可我们的场景:
|
||||||
|
|
||||||
|
- 客户端拿到配置后**本来就要在内存里解密成明文来用**
|
||||||
|
- 服务端是自己写的、自己部署的,本来就在信任边界内
|
||||||
|
|
||||||
|
**真正想防的「逆向」发生在客户端,而 E2E 加密的是客户端到服务端这一段。** 加密原语堆得再高级,防的都不是你要防的那个人。
|
||||||
|
|
||||||
|
## 点破:物理冲突与真正的命门
|
||||||
|
|
||||||
|
继续往下问,会碰到一个无解的矛盾:
|
||||||
|
|
||||||
|
> 「客户端要用凭据」和「客户端逆向拿不到凭据」,物理冲突。
|
||||||
|
|
||||||
|
客户端要用,就必须持有明文(至少在内存里);能防的边界只有**反编译静态分析**(99% 的场景),动态调试 dump 内存防不了--这条要诚实承认,任何声称「客户端防动态逆向」的方案都是自欺。
|
||||||
|
|
||||||
|
再回头看真正的命门:**就算凭据全部移到服务端,客户端代码里还硬编码着一个静态 API Key**。逆向者拿到这个 Key,照样能调用接口把全部凭据拉走。前面所有加密等于白忙。
|
||||||
|
|
||||||
|
结论浮出水面:**这个场景的安全关键根本不在加密,在鉴权。**
|
||||||
|
|
||||||
|
## 最终方案:最简三步
|
||||||
|
|
||||||
|
推翻方案 C 之后,剩下的东西少得可怜:
|
||||||
|
|
||||||
|
1. **删掉整个 E2E**:公钥上传、混合加密、双前缀,全删
|
||||||
|
2. **存储对称加密 + 后台脱敏**:数据库泄露不泄明文,界面回显一律 `****`--这一步防的是「数据库被拖」
|
||||||
|
3. **鉴权换动态登录**:工号 + 密码换短期 JWT,凭据经 TLS 下发、内存用完即弃、不落盘--这一步防的是「逆向拿到静态钥匙拉走全部」
|
||||||
|
|
||||||
|
删掉的代码比新写的还多。验证清单反而更短更硬:有 JWT 才能拿配置(无 token 401)、错误密码拒绝、存储确认为密文。
|
||||||
|
|
||||||
|
## 同一天的另外三次「推翻」
|
||||||
|
|
||||||
|
今天的主题像是「推翻自己」,不止这一次:
|
||||||
|
|
||||||
|
**二维码 404 双层修复**。弹窗打不开,第一层根因是 Bootstrap 5 移除了 jQuery 插件集成,八处旧写法全部改原生 API。改完发现页面还是坏的--真正的 404 在**用户实际访问的公网入口**:那边只反代了 PHP 请求,静态资源落在本地目录全是 404。教训:只测内网等于没测,验证必须覆盖用户真实入口。
|
||||||
|
|
||||||
|
**下载量统计全 0**。不是统计代码有 bug,是客户端走渠道轮转**直连渠道下载**,根本没经过服务端,自然不计数。修复不是改计数逻辑,而是补一个「下载完成才上报」的端点,顺手用 MD5 投票机制(三票阈值、三天窗口、投票人去重)让多个客户端互相校验文件哈希。
|
||||||
|
|
||||||
|
**32 条 FAIL 日志**。扫描出 32 条失败记录,逐条分类后:31 条是设计内的拦截(二选一城市待确认、多候选客户待澄清、在保不派单……),只有 1 条是真 bug--无操作人参数时误报「凭证过期」,实际该报「写操作必须指定操作人」。红色不等于故障,**先分类再动手**。
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
四件事,四个同款教训:
|
||||||
|
|
||||||
|
1. **先问威胁模型,再选加密方案**。E2E 防「服务端不可信」,数据库加密防「拖库」,动态鉴权防「逆向拉取」--三个威胁,三套解法,混着用只会互相拖累。
|
||||||
|
2. **鉴权优先于加密**。加密保护数据形态,鉴权决定数据归属。静态钥匙在手里,保险柜再厚也是空的。
|
||||||
|
3. **验证要覆盖真实入口**。内网全绿的系统,公网入口可能全是 404。
|
||||||
|
4. **最简方案往往更安全**。攻击面小、可审计、能讲清楚。复杂度本身也是一种漏洞。
|
||||||
|
|
||||||
|
> 方案 C 不是死于实现太糙,是死于**问错了问题**。安全设计的第一步不是选算法,是画威胁模型--搞清楚你到底在防谁。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*本文已脱敏,不含真实域名、IP、密钥或用户数据。*
|
||||||
Reference in New Issue
Block a user