加载中...
加载中...
-
说真的,看完这篇第一反应就是: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,正好匹配我现在的心情。
加载中...
加载中...

