C语言和C++语言是不安全的。美国国家安全局(NSA)希望广大开发人员使用内存安全的语言,因为大多数安全漏洞都是由内存使用方面的漏洞引起的。美国国家安全局网络安全部门主任Neal Ziring表示,所有程序员都在犯“简单的错误”,这些错误“依然完全非常普遍”。他谈论了缓冲区溢出和释放后使用漏洞之类的问题。他给出的建议是:改用像Rust这样的语言,Linux内核团队的一些成员正在这么做。

这是怎么回事?IT外媒The Register的撰稿人Laura Dobberstein在《NSA敦促组织使用内存安全的编程语言》一文中写道:
“C和C++特别成问题”……美国国家安全局(NSA)近日发布了指导方针,鼓励众多组织将编程语言从C和C++之类的语言转向内存安全的替代语言,即C#、Rust、Go、Java、Ruby或Swift……该政府机构主要担心的是,恶意网络威胁分子可能会利用管理不善的内存中的漏洞,这种情况在为程序员提供更多选项和灵活性的语言中出现得较频繁。……内存安全语言结合使用编译时检查和运行时检查,自动保护程序员避免引入后来变成漏洞的错误……从2020年第一季度到2022年第一季度,Rust用户数量增加了两倍。……NSA断言,C和C++特别成问题,这是一种流行的观点。微软Azure首席技术官Mark Russinovich在9月份亮明了其观点,他认为是时候停止用这两种语言编写的任何新项目了。但就在发推文前一晚,他本人却在85000行Sysinternals代码中添加了C/ C++代码。
照我说的做,而不是照我做的做?Fudzilla网站的Nick Farrell在其撰写的《NSA希望企业转向内存安全的语言》一文中补充道:“软件内存安全问题”NSA在“软件内存安全”网络安全信息表中表示,恶意网络威胁分子可能利用糟糕的内存管理问题来访问敏感信息、散布未经授权的代码执行风险,并造成其他负面影响。NSA网络安全部门主任Neal Ziring说:“在开发软件时,我们不得不一致地使用内存安全语言及其他保护措施,以消除这些弱点。”……微软和谷歌都表示,软件内存安全问题导致了大约70%的漏洞。糟糕的内存管理也会导致技术性问题,比如不正确的程序结果、程序性能逐渐下降以及程序崩溃。70%?Neal Ziring及朋友们进一步阐述了“软件内存安全”这个主题。“内存漏洞是可以防止的”现代社会隐式信任开发人员编写的软件按照预期的方式运行,而且不会被恶意攻击。但是可被人利用的软件漏洞仍然通常基于内存问题。例子包括内存缓冲区溢出以及利用软件如何分配和回收内存方面的问题。……C和C++等通常使用的语言在内存管理方面提供了很大的自由度和灵活性,同时又严重依赖程序员对内存引用执行所需的检查。简单的错误可能导致可被人利用的基于内存的漏洞……NSA建议尽可能使用内存安全语言。使用静态应用程序安全测试和动态应用程序安全测试来分析软件无法使非内存安全的代码完全做到内存安全。而且对于恶意威胁分子说,绕过地址空间布局随机化(ASLR)和数据执行保护(DEP)并非不可实现……如果使用内存安全语言以及现有的代码强化防御措施,许多内存漏洞是可以被防止、被缓解或者很难被网络威胁分子利用的。哇。C++专家对此又有何看法?实际上,据Hacker News网站上的用户tialaramex显示,他们基本上同意:目前WG21(“C++标准委员会”)的一系列最新文件包括不止一份称我们应该停止说C和C++是不安全的……,但很显然Bjarne Stroustrup和Gabriel Dos Reis的P2687特别惹人瞩目……它为程序员们往往编写糟糕的代码而感到苦恼。……Bjarne和Gaby将这种情况称之为“紧急情况”。(但P2687)并没有花太多的时间解释如何解决这个问题(只是含糊地提及“静态分析”)。……只有政府中有人把这类事情真正当成回事,这个实际问题才会得到解决。还是那句话,编写糟糕的代码是开发人员的过错。Slashdot网站的用户DaPhil称之为“明摆着的事”:这再明显不过了,但令人惊讶的是,过了这么久才指出这一点……大多数人都不擅长编写良好的代码。所以,我们从使用的语言中得到的帮助越多越好。可以用C语言编写良好的代码吗?当然!只是比用Java或Rust编写同样良好的代码要困难得多。那么说Java“安全”公平吗?The Register网站上留言读者Sitta_europea这样写道:Java到底又如何呢?……近期一些最重大的灾难不是用它编写的(log4j和npm)吗?你可以证明任何一种计算机语言的软肋。这是NSA必须攻克的难题吗?Hacker News网站上的用户Mark_l_watson是这么认为的:我觉得很悲哀,在911事件发生后,NSA和FBI显然停止了之前在公开支持计算机安全方面的大量工作……作为一名美国纳税人,我希望看到他们优先考虑这种工作。当然,我们以前遇到过这种情况。不妨听听Slashdot网站上的用户david.emery的观点:当然,从最初的Ada83版本开始,Ada就一直是一种有意设计的内存安全语言。而NSA在可验证的SPARK子集和证明工具方面做了一些工作。当美国国防部放弃Ada方面的大量投入时,失去了另一个机会,之所以放弃Ada,是由于“Ada不是行业在投入的”(好像“行业在投入”是一个理由)。……我记得NSA出席Ada会议的场景……80年代末,我出席了SIGAda会议,坐在旁边的那个人别着身份证件,上面写着他的大名和“美国国防部”。我对他说:“您准是在NSA工作。”他很不高兴,说“是啊。你怎么知道的?”“来自国防部的其他所有人都标明其所在机构。只有NSA标明美国国防部。”与此同时,The Register上留言的用户Anonymous Coward也许年事已高,愤世嫉俗:也许我年事已高,愤世嫉俗,但我忍不住认为与其说“几十年后才能清理这个烂摊子”,实际上还不如说“几十年后才能把这副烂摊子换成另一副不同的烂摊子。”

NSA 这波建议我看就是甩锅加标题党 原文说鼓励转向内存安全语言 媒体标题写成停止使用 C 和 C 加加 这差别大了去了 你说 NSA 真敢禁止 C 吗 它自己家一堆老代码怕不是还在用 C 跑 先把自己五角大楼的旧系统换了我再信 别天天发个 PDF 就让全世界程序员连夜重写 真当大家不用交房租啊 文章里 Neal Ziring 说所有程序员都在犯简单错误 这话我认 我年轻时候写 C 字符串复制忘了长度 服务器直接崩 老板站我背后骂了半小时 后来用 snprintf 加 review 加测试 就没再出过那种事 你说这是语言问题还是人的问题 都有 但你不能说用了 Rust 人就突然变圣人了 新手写 Rust 一样 unwrap 乱飞 unsafe 块乱塞 clone 到处复制 编译过了逻辑一坨 照样出漏洞 还有那个百分之七十 微软谷歌说内存问题导致百分之七十漏洞 这个数我信 但样本是他们的代码库 微软谷歌底层一堆 C 和 C 加加 攻击面最大的就是那些 你拿医院病人多就说医院危险 这统计不能直接推到所有行业 银行核心很多 Java 和 COBOL 漏洞多是反序列化和业务逻辑 网页前端漏洞多是 XSS 和供应链 你全换 Rust 那些洞就不存在了吗 想得美 我朋友做医疗设备 代码十年前写的 全是 C 跑得稳稳的 结果现在审查要他们写内存安全计划 还要评估换 Rust 的可行性 老板脸都绿了 换语言可以 钱呢 人没加 工期不加 认证还要重新做 飞机汽车医疗这些行业不是 Hacker News 上发个帖子就能换的 适航局车规认证 DO 178C 这些流程能把人拖死 你 Rust 工具链认证在哪 编译器认证在哪 出问题谁背锅 政府发指南的人背吗 文章里 Mark Russinovich 那个例子笑死 发推说停止新项目用 C 和 C 加加 前一晚自己给 85000 行 Sysinternals 代码加 C 和 C 加加 这不是虚伪 这是现实 他自己管那摊东西 要兼容老 Windows 要小要快要直接调 Win32 Rust 那套运行时和二进制大小当时不一定合适 所以嘴上说主义 心里是生意 大厂都这样 谷歌推 Go 推 Rust 自己还有巨量 C 加加 微软推 C Sharp 推 Rust Windows 内核还是 C 苹果推 Swift 底层 Darwin 还是 C 和 C 加加 听其言观其行 C 加加 标准委员会那些人发 P2687 说要停止说 C 和 C 加加 不安全 我理解他们护盘 毕竟被 NSA 点名谁都不爽 但这话有点掩耳盗铃 C 是真不安全 C 加加 看你怎么写 现代 C 加加 用 RAII 智能指针容器 能避免很多释放后使用 但历史包袱重 老代码多 新手乱用裸指针 一样死 你把 C 和 C 加加 绑一起骂 C 加加 社区当然跳脚 但 NSA 也没全错 它说 C 和 C 加加 特别成问题 这是断言 不是法律 谁对看代码看场景看团队 Rust 是好东西 我学了一点 借用检查器让我骂人 但编译过了确实放心 可它不是银弹 它有 unsafe 标准库也有洞 编译器也有 bug 依赖 crate 供应链更吓人 一个 build rs 就能跑任意代码 你信 crates io 吗 我反正不全信 大厂有审计小厂只能闭眼用 到时候出了事 又怪开发者 开发者又不是神仙 还有人说 Rust 用户从 2020 到 2022 增加两倍 增加两倍听着猛 基数多少 从十万到三十万 和 C 程序员比还是零头 而且很多 Rust 开发者搞区块链搞 Web 后端 真正嵌入式工控操作系统内核有多少 文章说 Linux 内核团队一些人用 Rust 对 但只是部分 主要是驱动和基础设施 Linus 也没说把 C 踢出去 内核核心还是 C 汇编 ABI 编译器架构绑得死死的 换语言不是换键盘 我表哥在汽车电子 他们代码全是 C 和 Simulink 生成 你让他换 Rust 车规 MCU 供应商不支持 AUTOSAR 全是 C 你换一个试试 老板直接让你滚 还有银行核心 很多 COBOL 老员工快退休了 新员工学 Python JavaScript 谁学 COBOL 更别说 Rust 现实世界不是 HN 评论区 现实是 Excel 加 C 加一堆祖传脚本 我不是说别用 Rust 新项目能用就用 云服务浏览器操作系统新组件用 Rust 挺好 Android 用 Rust 减少内存漏洞是真的 但那是综合因素 他们还有 Java Kotlin 大量代码 漏洞减少不能全归 Rust 而且 Android 团队有钱有人 小公司学不来 你让三个人的创业公司用 Rust 重写 招不到人 生态不熟 进度爆炸 最后公司死了 安全不安全还有啥意义 真正的安全是纵深防御 不是换语言就完事 文章也说了 ASLR DEP 能被绕过 那你还只靠语言吗 沙箱 最小权限 代码审计 模糊测试 静态分析 CI 加固编译器 硬件内存标记 CHERI MTE 这些都要上 C 也能加 AddressSanitizer UBSan Fuzzing 危险函数替换 严格 review OpenBSD 用 C 写得就很安全 靠的是审计和默认加固 所以关键在工程文化 不在语言标签 一个团队没测试没 review 没 CI 换 Rust 也会把逻辑写错 一个团队有严格流程 C 也能安全 当然 Rust 帮你拦内存错误 这很好 减少一类大风险 我支持关键模块用 Rust 但别搞语言宗教 别看见 C 就喊老古董 别看见 Rust 就喊救世主 工具是工具 菜刀能切菜也能切手 带护手的刀也会切手 只是少点 你不能因为有人拿菜刀砍人就禁菜刀 应该教人怎么用 还要监控 NSA 说简单错误依然完全非常普遍 这句话我信 但解决简单错误靠培训 工具 责任 不是发指南 如果 NSA 真想让大家都用内存安全语言 先出钱把 GCC LLVM 的 Rust 后端认证做了 把 crates 供应链管好 把培训教材翻译成中文 把嵌入式支持补全 光发信息表 然后媒体标题党 除了制造焦虑和流量 没啥用 政府项目最后变成打勾 安全审查问你们用不用内存安全语言 答 Java 可以 但 native 库还是 C 审查不管 这他妈就是形式主义 我朋友做政府项目就遇到过 主语言 Java 过审 底层 native 库 C 写的 漏洞照样在 审查只认标签 你说这指南到了基层会变成啥 变成采购要求 变成供应商合规负担 最后成本转嫁给纳税人 大厂有资源重写 小厂只能涨价或者倒闭 开源维护者本来就穷 你让他学 Rust 重写 谁付钱 NSA 付吗 不付就别说风凉话 再说内存安全语言也不是都安全 Java 有反序列化漏洞 C Sharp 有 unsafe Go 有数据竞争 Ruby 有 C 扩展 Swift 有 unowned 指针 所谓内存安全只是默认帮你管内存 边界还在 你乱来一样出事 而且逻辑漏洞 越权 支付漏洞 SSRF 权限配置错误 这些跟内存没关系 你全用 Rust 也挡不住 Equifax 是 Apache Struts 漏洞 SolarWinds 是供应链 不是内存 Log4j 不是内存 Shellshock 是解析 脏牛是竞争 Spectre 是硬件 换 Rust 解决不了 文章里说 C 和 C 加加 提供自由度和灵活性 又严重依赖程序员检查 对 这就是双刃剑 底层编程需要这种自由 你写操作系统分配器驱动数据库游戏引擎编译器运行时 很多时候就得直接碰内存 你用 Rust 也要 unsafe 只是把不安全集中起来 这比到处撒要好 但不是说 C 就该死 很多场景 C 还是最合适 资源少 芯片怪 工具链老 你 Rust 跑不起来 客户不等你 我修过一个 use after free 对象释放后日志还在打 指针悬空 随机崩 查了三天 换 Rust 确实编译不过 这个我认 但那个项目根因是生命周期设计乱 不是语言 C 加加 的 unique ptr shared ptr 也能帮忙 但 shared ptr 循环引用又泄漏 工具都有坑 所以别把任何一个语言吹成神 C 加加 委员会不想被骂不安全 可以理解 但 P2687 这种文件有点公关味 与其改说法 不如推广指南和工具 C 加加 Core Guidelines 说了很多
自己系统不也C写的?双标啊。
说真的,看完这篇第一反应就是:NSA这帮人是不是闲得慌?每年预算那么多,不想着怎么去追黑客,跑来教程序员写代码? 不过我仔细想想,他们说的那个内存安全的问题,确实是那么回事。我前两年在公司写了个C++的小工具,就一个日志解析模块,自测跑得好好的,结果上了生产环境,数据量一大直接崩。查了一下午,最后发现是缓冲区溢出了。我当时真的想骂娘,明明逻辑完全没问题,就是有个数组长度算错了。这种事在Java或者Go里面基本不可能发生,就算越界了人家给你抛个异常,不至于让整个服务挂掉。 但话又说回来,NSA这篇东西吧,怎么讲,有点“何不食肉糜”的意思。我知道Rust很牛,所有权机制确实能解决大部分内存问题,但问题是你让现有这些C++项目怎么办?好几百万行的老代码,你说换就换?很多银行核心系统还在用COBOL呢,你让他们换Rust?不现实嘛。 而且文章里面那句“所有程序员都在犯简单的错误”,我看了有点不舒服。是,我们是会犯错,但很多时候不是我们不想用安全的方式写,而是整个项目的时间表就卡在那里。用C++写,至少我调试起来熟,心里有数。你让我换一个新语言,光学习成本就顶好几个迭代周期。上头的人催进度,谁管你内存安全不安全。 还有那个Mark Russinovich,真是绝了。白天发推说别用C/C++了,晚上自己在Sysinternals里写C/C++代码。这不是打自己脸吗?我能理解他的意思可能是说新项目不要用,但你这个行为就很难让人信服啊。就像我一个朋友天天说减肥,但每次宵夜就数他吃得最欢。道理谁都懂,做起来就是另一回事。 说白了,C和C++这么难替换,是因为它们在系统级编程里确实强。你要写操作系统内核、写设备驱动、写高性能计算,这套东西还是最顺手的。Rust是想替代它们,但说句难听话,Rust现在最大的问题就是太磨叽了。借用检查器确实能防止你犯错,但有时候你都觉得你已经把逻辑想得很清楚了,它还是在那里“borrow”啊“lifetime”啊跟你较劲。写起来真的累。 不过有一说一,文章里说的那个70%漏洞是内存问题,这个数据我信。别的不说,我团队去年做安全审计,翻出来那堆CVE,十个里面有七个就是因为内存管理出的事。有些是前辈写的代码里埋的雷,有些是第三方库的问题。Rust生态确实好一些,至少编译器在编译的时候就能帮你挡住大部分这类坑。所以你说NSA这个建议没有道理吧,也不是。 但从“有道理”到“具体去落实”,中间隔着十万八千里。这几年我也面试过不少人,说实话C++水平过硬的人都越来越难招了,你还要人家会Rust?我又不是招什么超级赛亚人。而且团队里一帮人用了好几年C++,你让他们突然转Rust,项目进度直接就躺平了。管理层的KPI谁能扛住? 再说,Rust也不是万能的。你要是逻辑层面出了问题,用什么语言都没用。我记得有个哥们儿说过,你给程序员再安全的语言,他也总能写出不安全的代码来。这话糙理不糙。我们团队之前用Rust写了个模块,结果照样翻车。不是内存问题,是并发逻辑想错了,该加锁的地方没加锁,最后导致数据竞争。Rust再安全也防不住你脑子进水不是?所以NSA一味强调语言层面的事情,我觉得有点把问题简单化了。那些网络攻击者他们其实是坏,不是傻,你换语言只是提升了他们的门槛,并没有完全阻断他们的路子。 想起之前看到过一个段子,说美国空军把他们的导弹发射系统代码从C++换成Rust了。我当时就想,这要是在国内,换的时候肯定是堆加班堆出来的,中间不出几个事故都算是烧高香。之前我们银行换核心系统,用了大概一年多的时间过渡,中间各种兼容问题搞到所有人都快崩溃了。上线的头一个月,每天都会有莫名其妙的报错。有一次因为新系统一个字符编码的老问题,差点导致账务不平,吓得我们运维的同事连夜开会。你说这种大手术,是NSA一张嘴就能解决的吗? 不过说实话,我也慢慢理解了什么叫做“时代的趋势”。就像当年C语言替换汇编,Java替换C++做应用开发,语言一直在轮换。Rust这波确实是来势汹汹,因为吃到了云计算和基础设施领域性能需求的福利。你想现在数据中心里面大量的服务底层都是C和C++写的,但安全问题这么多年的确是是一个大难题。换成Rust至少节省了大部分排查内存问题的时间,让程序员把精力花在业务逻辑上,这对企业来说是实打实的优势。 但是我真的不喜欢那些“Rust吹”,只要一有人吐槽Rust难写,他们就会说“是你不够强”。操,程序员本来就是个不断妥协的职业,大家追求的都是在有限的时间和资源内把活干完。怎么到了Rust这儿就变成修行了?它那个学习曲线真的高得离谱,有些时候为了通过编译器的检查,你需要做很多额外的结构设计。我见过一个同事,用Rust实现一个简单的链表都折腾了两天,最后还是用了 unsafe。你说这不搞笑吗?安全语言用得自己上 unsafe,那还不如直接用C呢,至少我写 unsafe 的时候心里有数。 对,我刚才说到 unsafe 了。这正是另外一个角度,Rust其实也允许你写不安全的代码,只不过它把这个东西显式地标出来。但是只要程序员在项目里用了 unsafe,内存安全的光环就成了一个笑话。这就好比车里装了个限速器,但它有个开关,你一脚油门的时候自己把开关关了。那跟以前有什么本质区别呢? NSA那份指南里还提到了什么ASLR和DEP,说这些防御措施不是完全不可绕过。这话倒是真的。我以前看过不少安全方面的东西,发现攻击者总是能找到一些很偏门的角度去做攻击。你能做的只是尽量提高攻击的成本,让黑客觉得为了你这破系统去研究漏洞不值得。但你指望完全杜绝?不可能。只要有人,就会有漏洞,这跟用什么语言好像也不是绝对的关系。 当然我也知道有人会说“那你用C++写出来之后还不是一堆安全问题”,这个我承认。C++确实给了你太多的自由度,你一不小心就把自己吊死了。我当年用C++的时候最喜欢搞什么智能指针和手动内存管理,觉得自己特牛,结果现在回头去看,那代码里面至少有十来个隐患,庆幸还没被别人发现。后来又用了一段时间Rust,觉得编译器像是给你配了一个特别严格的私教,烦归烦,但确实能让你代码的每个决定都经过思考。 但是,决定一个项目用什么语言,还真不只是一个编程语言的纯粹技术问题。市场、招人、生态、维护成本、老板的偏好,每条都比“内存安全”来的更直接。我就想问NSA一句,你们有没有考虑过转型期带来的那些新bug?急急忙忙换个语言,代码重构、接口调整,万一中间出了什么差错,那可不就是新的漏洞?大家可能忘了 Windows Vista 当年为了安全那是改得多狠,最后兼容性翻车翻成什么样子,这难道不应该成为前车之鉴吗? 还有文章里提到C++标准委员会里的 Bjarne Stroustrup 和 Gabriel Dos Reis 的那个 P2687,虽然文章没具体说什么内容,但看那语气就觉得有意思。Bjarne 老爷子应该是觉得冤,毕竟C++这么多年了,安全问题是个系统工程问题、生态问题,你不能全赖在语言本身上面吧。他大概是想说C++也可以写出内存安全的代码,只是需要更好的工具和规范。这话也没毛病,毕竟牛人用汇编都能写出来安全代码,菜鸡用Rust一样写出漏洞满满的逻辑。 说到底,NSA出的这个指南,从技术方向上讲不算错,但执行的语境太理想化了。写系统软件的那帮老哥们哪儿是说转就能转的。与其花大成本把代码翻成Rust,不如多花点时间做代码评审、做安全测试、做模糊测试。这些手段对现有的C/C++代码来说,可能提升更快。把内存安全当作唯一目标,反而会忽略了整体安全。 当然,如果我是刚毕业的新人,要选方向,我可能也会去学Rust。毕竟这玩意虽然难写,但现在风头正劲,很多大厂都在推。从职业发展的角度来说,没啥坏处。但你要说因为NSA这句话,我就把手里C++的活儿全扔掉,那肯定不现实。 还有啊,我就说一句难听的,NSA他们自己秘密开发的那个什么开发框架,鬼知道是用什么写的?说不定内部大量还在用C和C++呢。一边劝别人别用,一边自己用得很爽,这种“照我说的做,别照我做的做”的事情,在他们那边也不是什么新闻。这次大概也就是出于某种政治需求,出来喊两嗓子,大家听听就好。 最后说回编程本身。其实语言始终就是个工具,安全不安全,关键还是用工具的人。我当年写C++翻车,是因为我水平不够,不是C++把刀架在我脖子上逼我用错。给我一把瑞士军刀,我也能把它用成凶器。反过来,你给我一把菜刀,我也不会拿来削铅笔。与其纠结用哪种语言,不如思考怎么在团队里推广安全意识、多写单元测试、多做代码审查,这才是最实际的事情。 反正,这篇文章我看完了,觉得写得还行,字里行间也是知道C/C++的问题在哪里。但NSA这个建议啊,听着很美好,落地的时候就是两码事了。我写代码这么多年,最大的心得就是,永远不要太迷信权威机构说的任何金科玉律。他们负责画饼,我们负责踩坑,这才是这个行业的本质。 好,我吐槽完了,继续去跟我的C++项目相爱相杀。今天它又给了我一个Segmentation fault,正好匹配我现在的心情。