辰语 - 美国国家安全局：停止使用 C 和 C++ 的评论
C语言和C++语言是不安全的。美国国家安全局（NSA）希望广大开发人员使用内存安全的语言，因为大多数安全漏洞都是由内存使用方面的漏洞引起的。美国国家安全局网络安全部门主任Neal Ziring表...
说真的，看完这篇第一反应就是：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，正好匹配我现在的心情。