笔记(常用标签)

审计代码

Claude Opus 5.5(High)#

通用的方法论可以浓缩成一句话:读代码是搜索,不是读书。 不要从第一个文件读到最后一个,而是带着具体问题,在代码里找证据。

下面这套流程不管你的目的是什么都适用。


总流程:目标 → 地图 → 锚点 → 追踪 → 验证 → 记录#

0. 把目标改写成可验证的问题#

"看懂这个项目"太模糊,没法判断什么时候算完成。要把它改写成一个具体的问题:

模糊的目标可验证的问题
了解 A 和 B 的关系A 在什么时候、通过什么方式、给 B 发了什么?
修一个 bug输入 X 时,哪一行代码让结果变成了 Y?
加一个功能一个类似的已有功能是怎么实现的?我要改哪几处?
学习架构一个典型请求从进入到返回,经过了哪些层?
安全审计外部输入从哪进来,最后流到了哪些危险操作(exec、SQL、文件)?
升级或迁移项目依赖了旧版本的哪些 API,分别在哪里?

问题越具体,后面每一步越不容易跑偏。 读着读着迷失了,就回来看看这个问题。

1. 建立地图(每个项目只做一次,15–30 分钟)#

不管目标是什么,先花一点时间回答下面五个问题,答案写在一页纸上就够:

  1. 它是什么? 读清单文件(package.json、composer.json、info.xml、pom.xml、go.mod……)和 README。
  2. 用了什么框架? 框架决定了"代码是怎么被调用的",下面单独讲。
  3. 入口在哪? 比如 main、路由表、CLI 命令、定时任务、事件监听、index.php。
  4. 目录怎么分? 通常一级目录看名字就能猜出职责,只需确认 2–3 个。
  5. 怎么跑、怎么测? 看 Makefile、scripts、CI 配置(.github/workflows)和 tests/。

另外有几个文件值得优先找:docs/、CONTRIBUTING.md、AGENTS.md/CLAUDE.md、ARCHITECTURE.md。作者亲自写的说明是信息密度最高的资料。

2. 找锚点:在代码里找到第一个落脚点#

有了问题之后,要找一个能直接搜到的东西作为起点:

  • 用户能看到的字符串:报错信息、界面文字、日志内容。这是最好用的锚点,直接全局搜。
  • 外部接口:URL 路径、命令名、配置项名、环境变量、数据库表名、端口号。
  • 名字:类名、app id、命名空间、文件名。
  • 测试:测试名往往直接描述了功能,测试代码本身就是"怎么调用它"的示例。
  • 历史:git log -S "关键词" 能找到引入某段代码的提交,提交说明往往会讲清楚它为什么存在。

上一次的分析里,锚点就是 app id richdocumentscode 和文件名 proxy.php。

3. 从锚点出发追踪#

追踪有三个方向,要有意识地选择:

  • 向上追:谁调用了我? 用 IDE 的 Find Usages,或者 grep 函数名。适合回答"这段代码什么时候会执行"。
  • 向下追:我调用了谁? 跳转到定义。适合回答"这段代码具体做了什么"。
  • 追数据:这个值从哪来,到哪去? 适合函数调用链断掉的时候。

调用链"断掉"的地方往往就是答案所在,常见的有这几种:

  • 配置、数据库、缓存(一边写,另一边读)
  • HTTP、RPC、消息队列(跨进程)
  • 事件、钩子、回调(发布者不知道订阅者)
  • 框架的依赖注入、路由注解、反射(代码里看不到显式调用)

遇到断点时,就换成追那个共享的值:key、URL、事件名、表名。

控制深度:每往下跳一层,都先问一句"这一层和我的问题有关吗?"。无关的函数只看名字和签名,猜一下它的作用,标记为"未验证",然后继续往前走。

4. 验证:区分"读到的"和"猜到的"#

纯读代码的结论,有一部分是推断出来的。手段从轻到重:

  1. 找第二处证据:比如一个调用点、一个测试、一份文档,能互相印证就够。
  2. 跑起来观察:浏览器 Network 面板、日志、strace/抓包。
  3. 加打印或断点:确认某段代码真的执行了、某个值确实是你以为的那样。
  4. 写一个小测试,或者改一行看会怎样:最直接的因果验证。

不是每条结论都要跑起来验证。但关键路径上的推断至少要有两处证据。

5. 记录#

每次分析结束时留下三样东西:

  • 一张链路图或流程图,每条线都标上 文件:行号
  • 结论清单,标明哪些已验证、哪些是推断
  • 未解决的问题

下次再看这个项目,或者把结论讲给别人听时,这份记录能省下大部分时间。


框架:最容易卡住新手的地方#

陌生项目难读,很多时候不是代码难,而是框架替你调用了代码,而你看不到调用方。所以遇到新框架时,先弄清它的"生命周期",也就是一个请求或事件是怎么进到你的代码里的:

要搞清楚的举例
路由怎么映射到函数注解/属性、routes.php、urls.py
对象从哪来依赖注入容器、构造函数自动注入
有哪些隐式入口中间件、事件监听、定时任务、生命周期钩子
约定的文件位置appinfo/、migrations/、templates/

上一次的分析就用到了 Nextcloud 的两条约定:#[FrontpageRoute(url: '/autosetup')] 对应 apps/richdocuments/autosetup,以及 OC.appswebroots 里列出已安装的应用。不知道这些约定,代码就会看起来"断了"。

做法:读框架官方文档里讲"请求生命周期"或"应用结构"的那一章,通常一小时以内就能读完,后面会省下很多时间。


按目的的侧重点速查#

目的锚点从哪找重点关注
找调用关系被调用方的名字、接口耦合方式,以及调用链断开的地方
修 bug报错信息、复现步骤先复现,再缩小范围,最后定位到具体的行
加功能最相似的已有功能照着它改;它涉及的文件就是你的修改清单
学架构入口和一个典型请求分层、模块边界、数据怎么流动
安全审计外部输入点输入一路流到 exec、SQL、文件、网络的路径,以及中途的校验
性能慢在哪(先测量)循环里的 IO、N+1 查询、缓存
升级或迁移依赖清单、弃用警告旧 API 的所有使用点

工具#

  • ripgrep / grep:第一工具。要学会用 -g 排除 vendor、node_modules、l10n,减少噪音。
  • IDE:跳转到定义、查找所有引用、调用层级(Call Hierarchy)。你的项目里已经有 .idea,PhpStorm 用好这三个功能就足够了。
  • git:git log -S、git blame、git log -- 某文件,用来查"为什么这么写"。
  • 调试器和日志:用于验证。
  • AI 助手:适合用来"快速建立地图"和"解释一段看不懂的代码"。但要让它给出 文件:行号 证据,并且自己去核对关键结论。

常见误区#

  1. 从头逐行读:读得很累,记不住,而且大部分内容和你的问题无关。
  2. 一路钻到底:每个函数都点进去看,三层之后就忘了自己在找什么。
  3. 只追函数调用:遇到配置、HTTP、事件就断了线。这时候要改追数据。
  4. 忽略框架约定:觉得"这个函数没人调用",其实是框架在调用。
  5. 把猜测当结论:没有核对就写进结论,后面的推理全建在沙子上。
  6. 在生成代码和第三方代码里浪费时间:vendor/、dist/、node_modules/ 一般跳过。

一个练习建议#

用同一个项目、换一个目标再练一遍,效果最好。比如:"在 Nextcloud 里点击打开一个 .docx 文件,到编辑器显示出来,中间发生了什么?"

  • 锚点:前端的打开文件逻辑(src/ 里搜 urlsrc),以及后端的 DocumentController
  • 会遇到的断点:iframe、postMessage、WOPI 回调(WopiController)
  • 产出:一张从点击到显示的时序图

你按上面的流程自己走一遍,有卡住的地方或者想核对结论,可以随时拿来问我。

谁把我灯关了
谁把我灯关了

网安小白,幻想着成为一名大佬。

版权声明
本文采用CC BY-NC-SA 4.0许可协议,转载请注明出处。
最后更新

相关文章

键盘快捷键

全站

CtrlK
搜索
/
搜索
?
显示快捷键
Esc
关闭弹窗、退出模式

阅读文章

T
目录
F
专注模式
A
批注模式

批注模式

P
笔
H
荧光笔
L
激光笔
E
橡皮擦
1–6
切换颜色
Shift
按住拖动画直线
CtrlZ
撤销
CtrlShiftZ
重做