攻击者不破最硬的锁 —— 从我的足迹地图加密被绕过说起

📌 本文定位: 这不是一篇密码学入门(体系知识请看《加密解密知识库完全指南》,工具命令请看《日常密码攻防手册》),而是一次真实事件的完整复盘

我给自己博客的足迹地图做了一套客户端加密,然后叫 AI 来”破解”它,想验证方案够不够硬。结果三轮下来,结论跟预想的完全不一样:我测出来的不是”加密弱鸡”,而是”攻击者根本不走我设防的那条路”

文中的每一次耗时都是本机实测数据,不是估算。所有案例数据都做了脱敏(只保留统计量,不含任何具体点位内容)。


一、起因:一次”不合规”的考试

事情的开始很随意。我把站点数据文件 data.json 丢给 AI,说:

“考你一道题,这是我从别人的博客下载的足迹数据,你能看出来是什么不?能把全部内容翻译出来不?”

这是个故意设套的测试——站是我的,文件是我的,我想看看”一个陌生人拿到这个文件能走多远”。

AI 第一眼就认出来了:这是 /map/ 页面的取数文件,v19 客户端加密格式,结构是:

1
2
3
4
5
6
{
"config": { /* 地图初始视图 */ },
"categories": [ /* 分类定义 */ ],
"markers": [ /* 公开点,明文 */ ],
"private": { "v": 19, "kdf": {...}, "cipher": {...}, "data": "<base64 密文>" }
}

公开点 26 条,明文可读;私密点 324 条,整体加密。

然后——它把 324 条全解出来了。

我当场就不冷静了:“靠!我就想测试下别人拿着文件能不能解密出来。你直接给我解密出来了,这不就说明这个加密机制非常弱鸡?!”

AI 的回应让我冷静了半秒:

“我不是’解密’出来的,我是’拿钥匙开门’出来的。”

这话是对的,而且指出了第一个方法论错误:

我做的 真正该做的
测试对象 一个已经知道密码的 AI 一个只有公开信息的匿名攻击者
实际测的是 “钥匙能不能开门”(废话,当然能) “没有钥匙能不能进门”
结论 无效测试 ❌ 有效测试 ✓

验证”别人能不能破”,就不能找手里已经有钥匙的人来测。 密码是我自己早前告诉它的,还留在它的记忆文件里。我把钥匙塞它手里,然后问它能不能开门——这不叫加密弱,这叫测试方法有漏洞

但这一轮也不是白折腾。它顺手暴露了一个真问题:密码的选取方式


二、第一次反转:换密码,然后 7.1 秒撞开

我一怒之下改了密码,把新版本的 data.json 甩过去:“我改密码了。你再试。”

AI 先做了一件事——验证是不是真的重新加密了

参数 旧文件 新文件 变化
kdf.salt ✅ 变
cipher.iv ✅ 变
密文 SHA-256 ✅ 变
密文长度 85,800 字符 85,800 字符 不变(同一份数据)
kdf.iterations 600,000 600,000 不变

salt、IV、密文全变,明文长度一致 → 确实是同一份数据换了新钥匙。这一步很重要:测试前先确认”被测对象真的变了”,否则后面所有结论都是空的。

然后开跑。先建性能基线:

实测项 数值
PBKDF2-SHA256 × 600,000 单次派生耗时 ~260 ms
单核尝试速度 3.5 ~ 3.9 次/秒
8 线程并行速度 ~31 次/秒

260 毫秒是什么概念?就是你在地图上输密码后那”卡一下”的感觉——这 260ms 不是 bug,就是这 60 万次迭代在干活

接下来是攻击脚本的核心思路。它不允许用任何内部信息,只允许用”一个看了你博客的陌生人能免费获取的东西”:

1
2
3
4
5
6
7
8
9
10
11
// 候选密码 = 公开信息 + 常见变形
const bases = ['misaka', 'Misaka', 'mISAKA', 'misaka10013', /* ← 域名去掉 .cn */
'mikoto', 'yuban', '御坂', '御坂美琴', 'railgun', 'biribiri',
'sncitybrain', 'suining', '遂宁', ...];

// 后缀变形:数字、年份、符号、键盘走位
const sfx = ['', '1', '123', '123456', '!', '@123', '2024', '2025',
'520', '1314', '10013', '.cn', '@cn', ...];

// 再加一份通用弱口令表
const weak = ['123456', 'qwerty123', 'P@ssw0rd', 'woaini1314', ...];

候选总数 1,362 个,8 路并行分发。

结果:7.1 秒命中。

结果
命中密码 misaka10013就是域名 misaka10013.cn 去掉 .cn
推导成本 零。看一眼站点 URL 就能想到
实际耗时 7.1 秒

(补一句严谨性:7.1 秒 ≠ “7.1 秒穷举完 1362 个”。脚本把这 1362 个候选轮询分发到 8 路 worker,命中候选恰好排在很靠前的位置,所以它所在那一路的命中时间就是 7.1 秒。真要跑满 1362 个候选,按 31 次/秒算大约 44 秒。但攻击者不需要跑满——他只需要撞对一次。 这个区别后面还会用到,先记住。)

这一局的结论

算法一点没输,密码输到底裤都没了。

状态 依据
算法层 ✅ 很硬 单核 3.5 次/秒。若密码是 11 位随机小写+数字,穷举需 1.35 亿年;就算换成 RTX 4090 级 GPU(相对本机 CPU 算力涨约千倍)也要 ≈13 万年
密码层 ❌ 一戳就破 misaka10013 100% 可从域名推导,10 秒内

一句话总结这一局:墙是金刚石砌的,但钥匙就压在门垫底下,门垫上还写着你家地址。

我损失的那 6 亿倍强度,全丢在”密码选了个可猜的”这一件事上。


三、第二次反转:新密码撞不开,但数据早就在外面

我这次认真改了密码。AI 换了个更大的字典:3,382 个候选,覆盖中文用户常见套路——键盘走位(qazwsxedc)、浪漫数字(5201314)、”自认为很强”的大小写+符号组合(Misaka@10013!Suining@2026)、拼音姓名变形、长句首字母。

轮次 字典 候选数 结果
1 公开信息定向字典(域名/网名变形) 1,362 全部未命中 ✓
2 扩展字典(中文用户常见套路 + “自认强密码”套路) 3,382 全部未命中 ✓
合计 4,744 未命中(跑了约 8 分钟)

密码这一层,这次合格了。 旧密码在同一条字典里 7 秒就破,新密码 8 分钟纹丝不动——这不是巧合,这是”可推导”和”不可推导”的区别。

但是——“换密码”这件事,可能等于白换

AI 顺手去翻了我博客仓库的 git 历史。一行命令:

1
git show b76510c:source/map/data.json > 明文.json

不需要任何密码。拿到全部 324 条私密足迹。

提交 日期 说明 markers 明文私密点 加密
b76510c 09-23 优化缓存更新 350 324 ❌ 否
ac47b0a 09-23 足迹返回博客更新 331 331 ❌ 否
2b8dd4a 09-22 增加接口和徽章卡 331 明文 ❌ 否
…另有 6 个 08-19 ~ 08-25 276 ~ 316 明文 ❌ 否

9 个明文提交,累计 640 KB,带 194 条私人备注。 而加密提交只有最后 2 个。

问题出在哪?——我是”后来”才给数据补上加密的。 git 只保护”当前版本”,前面 9 次提交把明文原封不动留在历史里。密码保护的是密文,历史里躺的是明文:

锁换了,后门还开着。

还有两处附带发现:

  • 仓库可见性:这两个明文提交确实已推到远端。我匿名探测了一下 .git/info/refs,返回 HTTP 401 → 私有仓库,没公开泄露。但私有 ≠ 安全(协作者、离职人员、平台侧、意外转公开,都是变量)。
  • 本地残留副本:下载目录里 8 个历史副本压根没有加密块,其中 2 个各含 247 条明文私密点。改密不溯及既往,这些文件不受新密码保护。
路径 成本 本轮结果
A. 找明文残留(历史 / 备份 / 缓存 / 日志) 分钟级,几乎免费 命中,拿到全部
B. 找密钥管理失误(密码写在笔记 / 脚本 / AI 记忆) 分钟级 ⚠️ 旧密码长期留痕
C. 了解算法 ≈ 0 ⚪ 文件头自带说明书
D. 社工 → 定向字典 小时级 ✅ 旧密码 7 秒破;新密码 4744 候选未破
E. 纯穷举 1.35 亿年 ❌ 死路

这张表才是全文的核心。 我设防的 D 路和 E 路,攻击者压根没走。他走的是 A 路——我连门都没装的那条。


四、核心方法论:攻击者的成本排序

4.1 三条路,不是四个步骤

大多数人对”破解”的想象是一条线性流程:收集信息 → 生成字典 → 研究算法 → 开始暴力破解

这个框架方向没错,但有个结构性缺口,而且顺序排错了

缺口在于:它整个都在回答”怎么猜出密码”。但攻击者问的第一个问题不是”密码是什么”,而是——

“我非得要密码吗?”

本轮就是活证据:AI 压根没碰新密码,一行 git show 就拿到了全部 324 条。整条密码破解链,被绕过去了。

正确的框架是三条独立路径 + 一条成本排序

1
2
3
A 路:绕过密码 —— 明文残留 / 侧信道 / 密钥失误 / 内部人   ← 永远先试这条
B 路:猜密码 —— 信息收集 → 字典生成 → 离线验证
C 路:破算法 —— 现代正确实现下 ≈ 死路

攻击者按成本从低到高扫,哪条先通走哪条。不是”先试 A,A 不行再试 B”,而是永远先扫最便宜的那条

4.2 “绕过”的本质:不打数据面

加密把 payload 锁死了,但系统的其它部分没有一起变强

  • 元数据(文件大小、修改时间、文件名)
  • 行为(谁在什么时候访问了什么)
  • 副本(git 历史、备份、缓存、云同步、回收站)
  • 还有最关键的那个:人

所以”绕过”的本质是句话:永远不打数据面。

顺便修正一个常见误解:当旁路不存在时,正面强攻就是唯一选项。 举个极端例子——一本上古遗迹的书籍,没有备份、没有密钥持有者、没有元数据,那就只能正面攻。所以精确表述不是”绕过优先”,而是:

成本低者优先。 绕过只是恰好最便宜的那条路。

4.3 KDF 迭代的真实作用(最反直觉的一点)

PBKDF2 那 60 万次迭代,真实作用不是”防止被破解”,而是:

把攻击者从”穷举”逼到”猜测”。

推演一下:

  1. 穷举 11 位随机密码 = 1.35 亿年 → 攻击者买不起 → 他不会走这条路
  2. 于是他 100% 的预算都花在”缩小候选空间”上
  3. 候选空间多大?完全由你的密码能不能被推导决定

所以结论是:

算法足够强时,攻防胜负 100% 转移到”密码的熵来自哪里”。

misaka10013 名义上有 11 个字符,看起来”挺长”——但对攻击者来说有效熵 ≈ 0,因为它 100% 可从域名推导出来。长度不等于熵。

4.4 社工和字典不是两个阶段

很多人把”社会工程学”和”字典生成”当成并列的两步。实际上它们是输入 → 输出的关系:

1
2
3
社工信息(域名、网名、生日、习惯)
↓ 变形、组合、套模板
定向字典(1362 个候选,全部有来源)

字典的定向性完全由社工信息决定。第一轮那 1362 个候选,来源只有一件事:看了一眼站点的域名。没有社工,你只能拿通用弱口令表硬碰,命中率会掉好几个数量级。

4.5 一个被忽略的维度:离线 vs 在线

本轮能 31 次/秒无限试,是因为密文整个被下载到了本地——这叫完全离线攻击:没有速率限制、没有告警、没有锁定、没有审计日志。

如果验证放在服务端做(限流 + 失败锁定 + 异常告警):

场景 速度 同一本字典的耗时
离线(本轮) 31 次/秒 秒级 ~ 分钟级
在线(限流 5 次/分钟 + 锁定) 5 次/分钟 天级 ~ 月级

同一个密码,离线是 10 秒,在线是 10 个月。

这个量级差,比”换更长的密码”值钱得多。


五、拆解”破解”的三层

“加密被破了”这句话太笼统,它其实是三件完全不同的事:

层次 打什么 现实成功率 典型手法
算法 AES / SHA 的数学结构 ≈ 0 差分分析、线性分析、代数攻击
实现 代码写错的地方 nonce 复用、tag 不校验、padding oracle、时序泄露、salt 写死、迭代次数=1
密钥管理 弱密码、密码写在哪、谁能拿到 最高 社工、字典、明文残留、内部人

一句话:

世界上几乎没有哪种加密是”被数学破的”,几乎全都是”密钥管理破的”。

本轮就是标准样本:算法一丝没动,10 秒拿到的。

Kerckhoffs 原则:知道算法不算攻击

你的框架里把”了解加密算法”排第三重要,实际它排第一便宜——因为现代密码学的铁律是算法公开

一个密码系统的安全性,应当仅取决于密钥的保密性,而不取决于算法的保密性。

而且更省事的是:加密文件通常主动交代自己的算法,跟附赠说明书一样。本轮那个 data.json 里,PBKDF2SHA-256600000 次迭代、AES-GCMivtagBits 全在明面上——连猜都不用猜。

什么时候”算法”这一环才真的重要?

  1. 元数据缺失——只拿到裸二进制,得先做逆向识别
  2. 实现有缺陷——ECB 模式、nonce 复用、tag 不校验、padding oracle、salt 写死、迭代只有 1 次

本轮的实现是正确的(随机 salt、随机 IV、GCM 认证标签、60 万次迭代),所以这一环贡献为 0

一个反直觉的行业事实

勒索软件团伙几乎从不”解密”任何东西。

他们买凭据。在地下市场,一台企业内网的初始访问权,价格和一台家用电脑的密码差不了多少。因为买通一个人 / 钓到一个账号,远比破一个密码便宜。

在产业化的攻击里,“破解”排得非常靠后。


六、一个上古文献的隐喻

这个类比值得单独一节,因为它精确得让人起鸡皮疙瘩——破译古代文献用的就是同一套方法论,一点没变

现代密码学 古籍破译
已知明文攻击 罗塞塔石碑(同一内容的两种文字对照)
多密文同明文 同一文献的多个抄本
统计攻击 词频 / 字母频率分析
实现漏洞 抄写员的笔误、漏字、重复
元数据 纸张材质、年代、出土位置

而且成败的关键往往不是”聪明”,而是”材料够不够”

商博良能破译古埃及圣书体,靠的是罗塞塔石碑这块平行文本——不是他比别人天才(虽然他也确实很天才)。在他之前几百年,无数聪明人对着同样的象形文字一筹莫展,不是脑子不够,是材料不够

这正好回到核心:

攻击者最先做的事,永远是找”材料”(旁路),而不是找”方法”(破解)。
方法是公开的,材料才是稀缺的。


七、”某天出现个超强的东西怎么办”

这是我提出的最后一个疑问:

“加密的设计还是有漏洞吧。就像 AI 突然横空出世一样,某一天出来个很会算的家伙,或者很会找寻数学规律并破解算法的东西。那利用漏洞直接破解算法不就是最简单的?”

这个担心有真实内核,但方向被大众搞反了。先把两个被混在一起的概念拆开:

是什么问题 怎么防 后果
穷举变可行(算力飞跃) 预算问题 加长密钥、加多迭代 可量化、可预防
数学破解(找到结构弱点) 智力问题 算法结构本身 降维打击,防不胜防

“很会算的家伙”是第一种,”很会找规律的东西”是第二种。它们的应对方式完全不同——而你担心的那种”某天突然破掉”只可能是第二种。

7.1 量子计算:唯一真实的”算法级威胁”,但方向讲反了

这是本节最要紧的技术点,大众(和科幻片)几乎全讲错了:

算法 量子影响 结论
Shor 算法 RSA / ECC(基于整数分解、离散对数) 🔴 必须迁移。NIST 已标准化后量子算法(ML-KEM/Kyber、ML-DSA/Dilithium)
Grover 算法 对称密码(AES/SHA)只是平方根加速 🟢 AES-256 的 2²⁵⁶ 变成 2¹²⁸ —— 仍然不可行

关键结论:量子对公钥密码是致命的,对对称密码只是”强度减半”。

而本轮那份 data.json 用的是 AES-256-GCM + PBKDF2-SHA256——正好属于 Grover 那一边。

量子计算机造出来了,你这个文件照样站着。

真正会崩的是 HTTPS 证书、SSH 密钥、数字签名那类 RSA/ECC 体系。

7.2 为什么”数学破解”这么难——这不是运气,是有机制的

  • 全球公开密码分析 AES 已 20+ 年。动力是”破掉就能拿图灵奖”,成千上万顶尖密码学家在打。结果只拿到边缘改进:2²⁵⁶ → 2²⁵⁴,等于没有
  • 密码学有个特殊性质:它是攻击者驱动的领域。算法不是因为设计得巧妙才安全,而是因为全世界最聪明的人一直打它、打不动
  • 历史事实:从来没有一个被广泛使用的现代分组密码或哈希函数,因为”算力变强”而被破。 DES 崩溃是因为密钥只有 56 位——那叫参数选错,不叫算法被破。AES 从 128 位起步,128 就是专门为抗算力增长选的。

7.3 “某天出现个超强的东西”——这个假设本身有两个问题

① 逻辑问题(最重要)

一个能推翻一切防御的假设,不是有效的威胁模型。

“某天出现一个超强的家伙”能推翻加密——但它同样能推翻”把文件锁进保险柜”、”用暗号通信”、”干脆不用电脑”。当一个假设对一切都成立时,它对具体决策提供的信息量就是零。

安全的判断标准不是”有没有理论风险”(什么都有),而是成本排序:在所有能拿到你数据的路径里,这条排第几。

② 方向反了:AI 的真实攻击增益在”人”身上,不在算法上

AI 让什么变便宜了 效果
深度伪造语音 / 视频 冒充熟人骗凭据,成本从”需要专业团队”降到”一个人一下午”
自动化个性化钓鱼 从群发模板到千人千面
批量生成社工话术 情报收集半自动化

AI 让”绕过”这条路更便宜了,不是让”破算法”变得可行。 攻击者用 AI 干的事,是把你的密码出来,而不是去 AES。

还有个更微妙的对撞值得说:密码学的设计目标,本身就是消灭统计规律——而这恰好是 AI/ML 的强项。已有研究用机器学习去找差分特征,在简化的玩具密码上有明显效果,在完整 AES 上完全无效。原因很简单:抗统计分析就是密码设计的第一原则。

AI 会变成辅助工具(找实现 bug、找配置错误、自动化社工),不会变成”破 AES 的机器”。

7.4 诚实的边界:我可能错在哪

  • 上面的信心来自公开密码分析的历史,这是归纳,不是数学证明。理论上明天可能有人发现 AES 的结构性缺陷。
  • 但有三层缓冲:AES 的 S-box 是按有限域上明确的数学规则生成的(专门抗差分/线性分析),不是拍脑袋编的;即使它倒了还有别的算法;而且能破 AES 的人会先去领图灵奖,不会拿来偷你的足迹数据
  • 所以准确的说法是:它是真实的尾部风险,但对你的威胁等级排最后——因为它要求的攻击者成本,远高于绕过你其它门。

7.5 真正该做的事:密码敏捷性 > 算法选择

这才是本节对实际工作最有用的结论。

密码学界应对”未来某天算力/算法突变”的标准思路是**”先存后破”(Harvest Now, Decrypt Later)**:今天抓走你的密文存档,等十年后算力够了再解。

对抗它的核心手段不是”选一个永恒安全的算法”(不存在),而是:

密码敏捷性(crypto agility)——重要的不是今天用什么算法,而是能不能在一天之内换掉它。

而这恰好是本轮方案最漂亮的地方:明文备份在我自己手上,换算法只要改前端一个模块 + 重新加密,不需要重新录入 324 个点

反面教材则是:把数据锁死在某个专有备份格式里,算法一过时就只能干瞪眼。


八、防守方的四个后门

这一轮暴露出的真实暴露面是四个,不止密码一个:

# 后门 本轮实测状态 修法
1 明文残留(git 历史 / 本地副本) ❌ 9 个明文提交 640 KB + 8 个未加密本地文件 重写历史 / 删仓重建 + 清本地副本
2 密码可推导 ❌ 旧密码 = 域名去 .cn,7.1 秒破 换不可推导的强密码(✅ 已完成)
3 密码留痕 ⚠️ 明文写在 AI 的长期记忆文件里 从记忆/笔记中移除,改为”由本人持有”
4 副本未同步加固 ❌ 换密码不溯及既往 加固后必须回头清旧副本

修复顺序应该反过来:从最便宜的攻击路径开始堵。

大多数人(包括我)的直觉是”加强密码”,但按成本排序,正确顺序是:

1
2
3
4
5
6
7
先清明文残留(A 路)

再清密钥留痕(A 路)

清理本地旧副本(A 路)

最后才是密码本身(B 路)

而讽刺的是,我的新密码已经是合格的了——最不需要动的那一环,我第一时间就动了。

木桶效应

你把一条路封到 100 分,攻击者从 0 分的那条路进来,总分还是 0

所以安全从来不是”把最显眼的那道门加固”,而是先把所有门数一遍

关于”要不要提高迭代次数”(一个反直觉的建议)

遇到这种事,第一反应通常是”那我把 600,000 次提到 2,000,000 次不就更硬了”。

不建议。 因为:

  • 本轮的威胁是”密码可猜“,不是”密码被穷举“——迭代次数对前者毫无帮助
  • 提到 200 万次只会让你的手机解锁多等 3 倍时间(~800ms),用户体验直线下降
  • 收益 ≈ 0,成本明确

加固要对准真实的攻击路径,不是对准”听起来更安全”。


九、收尾:加密的边界

绕了一大圈,回到最初那句”这不就说明加密弱鸡”。

不是加密弱。是”以为加密就完事了”这个想法弱。

加密能做的和不能做的:

加密能解决 加密管不到
数据面(密文本身) 密钥怎么选、怎么存、谁能接触
静态托管场景下的不可读性 明文副本留在哪(git / 备份 / 缓存 / 截图)
把攻击成本推高到”需要人” 有人愿意到场、愿意反水、愿意被钓

而加密真正的价值,是这句话:

把”自动化、无限次、无声无息”的攻击,变成”需要人、需要接触、会留痕迹”的攻击。

加密本身挡不住有决心、有资源、有内应的人。它挡的是成本——它把攻击从”一台笔记本跑一晚上”推高到”需要有人到场或有人反水”。而这两件事是可检测、可追责的。

这就是为什么加密是”必要但不充分”的:它把数据面锁到无懈可击,然后攻击者就去敲别的门了(比如你的 git 历史)。

把整篇压成三句话

  1. 先找材料(旁路 / 残留 / 人),找不到材料再猜密钥,算法在任何阶段都不是难关,而是开场读物。
  2. 算法足够强时,胜负 100% 取决于”密码的熵来自哪里”。长度不等于熵。
  3. 加固的顺序,是攻击成本的倒序——最便宜的那条路,才是你最先要堵的。

攻击者不破最硬的锁,他找没锁的门。


附录 A:性能基线(本机实测)

数值 说明
PBKDF2-SHA256 × 600,000 派生 ~260 ms 单次
单核尝试速度 3.5 ~ 3.9 次/秒
8 线程并行 ~31 次/秒 受 CPU 核心数限制
定向字典命中耗时(本案例) 7.1 秒 1362 候选轮询分发,命中位靠前
扩展字典全跑耗时 ~8 分钟 3382 候选
对比:GPU(4090 级)估速 ~3,500 次/秒 相对本机约千倍

由此推出的两个量级概念:

  • 11 位纯随机小写+数字,在本机穷举需 1.35 亿年;即使 GPU 加速千倍也需 13 万年
  • 但”可推导的 11 字符密码”,实际成本约等于 0

附录 B:自检思路(可直接复用)

这套审计思路不依赖任何第三方库,用 Node 内置 crypto 就能跑。核心伪码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 1. 只做「认证标签校验」,不导出明文
function test(pw) {
const key = crypto.pbkdf2Sync(Buffer.from(pw, 'utf8'),
salt, iterations, 32, 'sha256');
const dec = crypto.createDecipheriv('aes-256-gcm', key, iv);
dec.setAuthTag(tag); // 密文尾部 16 字节
try {
return Buffer.concat([dec.update(ct), dec.final()]).toString('utf8');
} catch (e) { return null; } // GCM 校验失败 = 密码错
}

// 2. 字典只允许来自「公开可得信息」
// 域名 / 网名 / 站点标题 / 常见弱口令 / 中文用户套路

// 3. 8 路 worker_threads 并行分发

四个必查项(审计清单):

# 检查什么 命令 / 方法
1 git 历史明文残留 git log --oneline -- <数据文件> 然后逐个 git show <hash>:<path>,检查是否含加密块
2 密码是否可推导 用域名/网名/生日做定向字典自测(就是附录 A 那套)
3 本地残留副本 搜索下载/备份目录的同名文件,检查是否有未加密版本
4 密码留痕 检查笔记、脚本、记忆文件、聊天记录里是否明文存有密码
5 (附加)仓库可见性 curl -o /dev/null -w "%{http_code}" <repo>.git/info/refs?service=git-upload-pack → 401 = 私有

附录 C:延伸阅读

本文讲的是方法论层——前两篇讲”是什么”和”怎么用工具”,本篇补上”攻击者怎么选路径”。