父母给我买了电脑的那一刻,我就想要第二台。十几岁的时候,我买不起,但几年后,当我升级到更新的机器时,我保留了旧机器。现在我有两台机器,一切安好。这是我一生保持的习惯。从那时起,我总是有两台机器,有时甚至三台。我可以使用两个互联网连接,一个是光纤,另一个是 5G。这就是冗余。当然,第二台机器总是慢一点,备用网络连接也是如此。这就是优雅降级。CrowdStrike 更新暴露了全球范围内缺乏的这两种业务和软件工程故障保护,并使有缺陷的应用程序(驱动程序)更新处于非常不同的境地。
微软 Windows
自 Windows 3.1 以来,我很不幸地使用了所有版本的 Windows。我说不幸是因为在 Windows 3.1 和 Windows 10 之间,我对这个操作系统的体验从未有过积极的影响。它有一些好的时候——比如 Windows XP 或 Windows 7——但最终,我一直认为它只是必要之恶,甚至在转向 Linux 和 macOS 之前也是如此。如果说有一个问题困扰着所有版本的 Microsoft Windows,那就是不稳定。我记得大约每隔几个月就必须重新安装 Windows 98SE,或者在 Windows XP 上运行系统还原的频率也差不多,但在 XP 上至少我有这个选择。
作为 Windows 用户,您每天打开计算机时都会感到轻松,一切都按预期加载。您真幸运!
随着网络的出现,安全性成为一个重要的考虑因素,我们很快发现自己必须比更换桌面壁纸更快地安装防病毒软件。稳定性突然变成了次要问题,因为在没有反恶意软件的情况下将 Windows 连接到网络肯定会在几分钟内使您的机器无法使用。事实上,在 Windows 7 的鼎盛时期,我就是做了这个实验。我在刚刚深度格式化的硬盘上安装了正版 Windows 7(正版磁盘,从商店购买密封的),连接以太网电缆,打开我的电脑,打开 Internet Explorer,除了内置主页外没有导航到任何网站,并将其放在那里大约一个小时。一小时后,我继续从官方网站下载 Avira,运行扫描,它检测到了恶意软件。没有什么严重的问题,但尽管如此,我还是被感染了。
Microsoft Windows 从来就不是一个安全的操作系统。它只是一座用纸牌搭建的房子。
任何像微软 Windows 这样流行的操作系统本质上都不太安全的说法正是微软及其辩护者喜欢宣扬的谬论和神话。任何安全专家都会告诉你,这种说法有一定的道理,但基本事实是一样的——Windows 是为非网络桌面环境创建的,这个基础没有改变,即使在 Windows 11 中也没有改变,即使运行它需要某些安全硬件 (TPM)。
正如 Computerworld 的 Steven Vaughan-Nichols 在 2009 年所说的那样,“归根结底,Windows 的所有安全改进都只是在其致命的单用户、非网络基因上增加了一层安全保护。”这种趋势并没有真正改变。微软为提高 Windows 安全性所做的一切——虽然值得欢迎——归根结底都是被动的修补,甚至他们似乎也不真正相信这一点,正如官方关于如何绕过 Windows 11 的 TPM 要求的说明所表明的那样。
从软件工程师的角度来看,微软 Windows 看起来就像是一堆技术债务,需要彻底重写。
鉴于 Windows 架构如此糟糕,CrowdStrike 的安全方法并不令人意外。作为一家致力于在商业和企业安全领域占据领先地位的科技公司,选择超越传统防病毒软件方法(即在机器启动后加载应用程序)并安装驱动程序以在机器启动前运行,这是有道理的。这听起来有点小题大做,但事实并非如此。我们谈论的是 Windows。Windows 迫切需要这么多支持才能成为一个安全的环境。MacOS、iOS、Linux 都将安全性融入到操作系统的基础中。我们谈论的是 BSD Unix,归根结底,它的核心是采用非常不同的设计理念开发的。
Microsoft Windows 对第三方安全解决方案的根本需求是全球 IT 崩溃的核心原因之一。再加上其他架构缺陷(例如损坏的文件可能导致整个操作系统崩溃),整个世界将陷入瘫痪。
设计不佳,缺乏冗余
当我在 M3 Max MacBook Pro 上输入这篇文章时,旁边还有一台随时待命、充满电的 M2 MacBook Air,两台设备运行的 macOS 版本都不一样。在一台设备上,我启用了自动更新,而在另一台设备上,我没有启用,因为不管你信不信,苹果也会造成很多麻烦。在两台设备上运行不同版本的 macOS 不仅可以让我的工作保持连续性,还可以为另一台设备提供潜在的恢复解决方案。
我十几岁时提出的冗余理念被我的朋友认为是极端和不必要的,但几年后,当我参加思科认证时才发现冗余是每个可靠网络的核心。事实上,可靠性和冗余都内置在 TCP 和 UDP 协议中——这是使用正确工具完成正确工作的另一个很好的软件开发示例。在这种情况下,当这是主要目标时,TCP 会确保数据完整性而不会降低性能,而 UDP 会确保您的 Zoom 对话等内容在数据包丢失时不会中断。
虽然 TCP/IP 从根本上来说不可靠,但它也采用了一些机制来避免其核心缺陷成为自我衰弱的缺陷。
在软件工程中,一旦我们了解并理解了系统的潜在缺点,我们就会接受开发故障保护措施的培训。处理错误或未知情况应该或多或少是任何编写代码的人的第二天性。让我用伪代码编写一个小但很有说明性的例子,即使是普通人也应该理解:
<span style="background-color:#f9f9f9"><span style="color:#242424"><span style="color:#aa0d91">if</span> (月份<span style="color:#aa0d91">中的</span>天数) === <span style="color:#1c00cf">30</span> {
<span style="color:#aa0d91">return</span> [四月, 六月, 九月, 十一月]
} <span style="color:#aa0d91">else </span> <span style="color:#aa0d91">if (月份</span><span style="color:#aa0d91">中的</span>天数) === <span style="color:#1c00cf">31</span> {
<span style="color:#aa0d91">return</span> [一月, 三月, 五月, 七月, 八月, 十月, 十二月]
} <span style="color:#aa0d91">else </span> <span style="color:#aa0d91">if</span> (月份<span style="color:#aa0d91">中的</span>天数) === <span style="color:#1c00cf">28</span> {
<span style="color:#aa0d91">return</span> [二月]
}</span></span>
上述代码可以很好地运行 3 年 11 个月,但第 4 年 2 月除外,因为该月有 29 天。这是一个未处理的情况,程序会说“我不知道现在该做什么,我要停下来并抛出一个错误” ——例如,想想你的 Windows 蓝屏死机。
一个解决方案是添加闰年的额外情况,或者更安全的选择是使用 else 情况处理所有其他数字,如下所示:
<span style="background-color:#f9f9f9"><span style="color:#242424"><span style="color:#aa0d91">if</span> (月份<span style="color:#aa0d91">中的</span>天数) === <span style="color:#1c00cf">30</span> {
<span style="color:#aa0d91">return</span> [四月, 六月, 九月, 十一月]
} <span style="color:#aa0d91">else </span> <span style="color:#aa0d91">if (月份</span><span style="color:#aa0d91">中的</span>天数) === <span style="color:#1c00cf">31</span> {
<span style="color:#aa0d91">return</span> [一月, 三月, 五月, 七月, 八月, 十月, 十二月]
} <span style="color:#aa0d91">else</span> {
<span style="color:#aa0d91">return</span> [二月]
}</span></span>
因为如果有人突然觉得闰年太麻烦了怎么办?我们就一直坚持 2 月是 28 天,让世界自己承担 730 年后仲夏圣诞节的后果吧。虽然这听起来很荒谬,但我的程序无论如何都会成功,因为所有情况都已考虑在内,我们再也不会看到蓝屏死机了。
在导致全球系统瘫痪的 CrowdStrike 漏洞中,我发现了两个基本的软件工程错误:
- 显然,Microsoft Windows 并未涵盖驱动程序中损坏文件的情况。如此简单的错误竟然没有得到妥善处理,这让人难以置信,但显然并非如此,这让我想知道——不幸的是,其他所有恶意行为者也是如此——还能做些什么来在操作系统中造成同样灾难性的后果?单个损坏文件不应该导致整个操作系统崩溃,但在 Windows 的情况下,它却会。过于乐观的错误处理始终是糟糕的软件开发实践和糟糕设计的标志。
- 操作系统中没有恢复机制。我对 2024 年的计算机的期望是,如果更新出错,它知道如何恢复到最新的工作版本。恢复是我们在软件开发中经常做的事情。即使你做对了,有时也会发现需要“撤消”的错误。作为操作系统,我希望它能够识别更新的错误版本并将自身恢复到以前的工作状态。例如,在 Mac 上,即使你删除了硬盘驱动器的全部内容,机器也会意识到它没有操作系统,要求你连接到网络,并下载最新的工作版本。在这种情况下,Windows 所需要做的就是认识到它无法正常启动——它确实无法正常启动,因此出现 BSOD——并加载它最后记得可以工作的版本,或者至少将自身加载到安全模式,并建议用户手动选择以前的版本。
CrowdStrike 更新出现故障是不幸的,而且大多数情况下是可以避免的,但正如每位软件工程师都知道的那样,生产中难免会偶尔出现重大错误。我还没有遇到过没有造成重大事故的软件开发人员。这就像外科医生在手术台上看到有人死去一样。这是不可避免的。我曾经因为前端和后端的密码长度不匹配而将数百万学生拒之门外。
凡是可能出错的事情,就一定会出错。——墨菲定律
在软件开发中,我们还需要听到多少次这种话才能认真对待它?这一次,微软显然没有考虑到这种可能性,这引出了一个问题——为什么?作为一个没有任何真正垂直整合的操作系统所有者,它非常清楚,并且几十年来一直知道,它在几乎无法控制的环境中运行其操作系统。系统集成商会将他们能找到的任何硬件都扔给它,而开发人员会将他们能找到的任何东西拼凑起来。那里就像狂野的西部。我的第一份工作是在当地的电脑商店为客户组装电脑。硬件很糟糕,客户想要安装的软件更糟糕。在微软,防御性编程似乎在很大程度上仍然是一个陌生的概念。
但当然,我们不能把所有问题都归咎于微软。受影响的企业同样有错。关键任务系统运行时没有考虑冗余或优雅降级。这也不是第一次了。737 Max 的悲剧与依赖单个传感器运行软件有关,该软件完全按照指令运行,但由于没有硬件和数据冗余,它所做的与飞行员的预期相反。
2024 年,整个银行系统、机场、国家医疗保健和交通系统怎么能依赖单一操作系统呢?冗余在哪里?!
这种规模的 IT 系统不应该在没有内置冗余的情况下存在。仅仅有发电机是不够的。当然,这有帮助,但随着我们生活在一个日益数字化的世界,这远远不够。走进一家大医院,到处都是 BSOD,这简直是最糟糕的情况了。事实上,一些常见的假设的答案应该是这样的:
- 如果停电了怎么办?我们切换到电池供电,同时发电机启动以补充我们的电力储存。✅
- 如果互联网断线了怎么办?我们会切换到其他光纤提供商,然后是 DSL 或 5G 提供商。如果没有可用的,我们会切换到卫星通信。✅
- 如果操作系统在网络上崩溃了怎么办?我们使用备用操作系统重新启动。✅
这些是我期望的最低限度的答案。显然,遗憾的是,我们生活在一个 IT 基础设施的世界里,而这些都不是现实,我的期望似乎超出了科幻电影的范围,原因总是一样的——成本太高——这让我想到了下一个观点。
廉价文化
这远远超出了 Cory Doctorow 著名的“enshittification”理论。软件在没有任何优雅降级的情况下正在退化。东西要么有效,要么无效。微软和 CrowdStrike 并不是唯一的例子,说明软件在快速、廉价地开发时会变得多么糟糕。我同样担心苹果的 OpenAI 集成,事实上,任何最近通过 OpenAI 的 API 添加“AI 功能”的软件公司,因为我几乎可以保证,一旦他们的 API 遇到问题,一旦他们的服务器受到 DDoS 攻击、崩溃或发生其他任何事情,一半的软件都会非常糟糕地处理这些中断。
软件越来越不可靠,因为它越来越不像一个独立的实体,而更像是一个相互连接的服务网络。本质上,软件即服务和集成并没有什么问题,但它是一个极其微妙的链条,通常只要有一个环节出现故障,一切都会崩溃。另一种选择是拥有一切——这可能不现实——或者实施冗余和体验,即使退化,也能提供足够的价值。不过,这两种解决方案都不便宜。
我记得当渐进式 Web 应用 (PWA) 成为热门的新生事物时,每个人都在不断使用优雅降级,就像它是标点符号一样。它与“协同”和“创新”一起成为流行语中的流行语,在面试中脱颖而出的候选人是在说“你好”和问“你好吗?”之间没有提到 PWA 的人。然而,我们发现,同样的流行语已经失去了流行度,甚至苹果也对其竖起了中指,而且几乎没有任何网站仍在离线模式下加载内容。
我们正在应对整个软件开发行业一种令人衰弱的廉价文化。
敏捷是另一个很棒的流行词,但我们确保将敏捷也改造成它不应该被解释为的东西——未经测试的快速交付。事实上,敏捷的整个概念是为了实现快速转型,而不是快速构建。如果你只考虑 IT 预算,那么运行没有冗余的 IT 基础设施无疑更便宜,但总的来说,它真的便宜多少?
CrowdStrike 的不幸失误将产生长期影响。数百人可能面临诉讼,保险公司可能忙得不可开交,工作可能丢失,假期可能毁于一旦,等等。这样一个小失误的连锁反应将异常巨大和深远,我不禁想到,如果再多一点投资,就能在很大程度上防止这场灾难。
岌岌可危的未来
当全世界都在愤怒地指责 CrowdStrike 及其首席执行官乔治·库尔茨时,我觉得他们应该把目光转向别处。持续不断的快速廉价软件、不断的补丁、完全疯狂的商业行为,这些行为都依赖于祈祷而不是硬件和软件冗余;再加上整个科技行业越来越缺乏测试的软件更新,这就是我们走到这一步的原因。这不是一次损坏的更新。
在这种情况下,我必须问,我们确定我们仍然希望在软件和日常生活中使用人工智能吗?我们确定我们想要为悬而未决的软件增加更多复杂性吗?我们显然甚至无法做好和足够可靠地完成简单的事情。也许我们应该回到基础,在编写好的软件时要考虑简单性、冗余性和优雅降级。怎么样?我们可以这样做吗?拜托?