游客发表
2014年4月,OpenSSL被曝出一个叫“心脏出血”的漏洞漏洞。攻击者能远程读取服务器内存里最多64KB的漏洞数据——私钥、会话令牌、漏洞用户密码,漏洞什么都可能被捞出来。漏洞代码本身只有两行,漏洞错得离谱。漏洞问题出在memcpy调用时没检查长度字段是漏洞否被伪造。就这么简单。漏洞但真正让人后背发凉的漏洞是:这段代码已经存在了两年多,无数人审过,漏洞没人发现。漏洞
漏洞很少是漏洞“写错了”三个字能概括的。它更像一个系统性的漏洞失误,藏在人的认知边界、项目的资源分配、以及时间积累的债务里。
我见过一个支付系统的越权漏洞。开发者把用户ID直接拼进了SQL查询,但加了WHERE user_id = ?。看起来没问题对吧?可那个?是从cookie里取的,而cookie里的user_id是Base64编码的。攻击者把user_id=1改成user_id=1 OR 1=1再Base64一下,后端解码后直接拼进查询。开发者以为参数化查询就安全了,却忘了参数本身可能被篡改。这种漏洞的原因不是技术不够,是假设和现实脱节。你假设cookie里的东西是可信的,但攻击者不这么想。
人脑的短期记忆容量有限。写代码时你脑子里装着业务逻辑、接口定义、异常处理、性能指标——这时候安全往往排在最后。不是不重要,是认知带宽被占满了。心理学上叫“认知隧道”,你盯着眼前的功能实现,视野之外的边界条件全成了盲区。缓冲区溢出、整数溢出、竞态条件,这些漏洞的根子都在这里。写代码的人不是不知道有溢出这回事,是当时没想起来。
项目管理和漏洞的关系比大多数人想的更紧密。一个三人团队维护着二十万行代码的遗留系统,每周还要接新需求。安全补丁?排期排到三个月后。代码审查?走个形式,点个“LGTM”就合并。这不是态度问题,是结构性资源匮乏。你让一个人同时追五只兔子,他一只也追不到。漏洞在这种环境里不是“会不会有”的问题,是“什么时候爆”的问题。
时间也是帮凶。一个漏洞今天不可利用,不代表明年还不可利用。新的利用技术、新的攻击链、新的辅助工具,会让原本“理论上存在但实际打不了”的漏洞变成真枪实弹。比如strcpy溢出,在ASLR和DEP普及之前很难利用,但2000年代初出现ROP技术后,这类漏洞的杀伤力直接翻倍。写代码的人当年可能评估过风险,觉得“溢出就溢出吧,顶多崩一下”,但他没法预测五年后的攻击技术。
漏洞是代码在特定环境下的“意外行为”。环境变了,行为就变了。
还有一种情况:抽象泄漏。你用高级语言、用框架、用云服务,以为底层帮你兜底了。但抽象层本身有裂缝。比如某个ORM框架默认把用户输入当字符串处理,你传个数组进去,它拼接SQL时数组变成逗号分隔的字符串——注入就来了。你信任了框架,框架信任了你的输入,你的输入来自用户。链条上每一环都觉得自己没问题,但合起来就是问题。
最后说个反直觉的:有些漏洞是因为代码太“正确”了。某个加密库严格按照RFC实现,但RFC本身有个模糊地带,不同实现理解不同。A库认为这个字段是可选,B库认为是必选。交互时B库解析失败,回退到不安全的默认模式。漏洞不在代码里,在规范与规范之间的缝隙里。这类漏洞最难修,因为得先说服标准委员会。
漏洞的原因从来不是单一的。它是人、时间、资源、抽象、规范五股绳子拧在一起的结果。想减少漏洞,光靠“写代码时小心点”远远不够。得承认人会累、会忘、会假设错误,然后用工具、流程、冗余检查去补这些洞。编译器警告全开、静态分析跑起来、模糊测试常态化、代码审查别走过场——这些老生常谈的东西,每做到一条,就少一类漏洞。
但别指望漏洞会消失。只要系统还在演化,只要人还在写代码,漏洞就会换个样子冒出来。我们能做的,是让它们少一点,再少一点。
```友情链接