美机构:立即停止使用C和C++

对于嵌入式软件工程师来说,C/C++语言是最常用的编程语言之一,它是一种高效、简洁、灵活的编程语言,尤其在嵌入式、单片机领域,它创造了许多奇迹,包括智能手机、家用电器、汽车或是医疗设备。

但每每提及“安全”问题时,大部分人便将C/C++划在围城之外。12月6日,美国网络安全和基础设施局 (CISA)联合美国国家安全局(NSA)、美国联邦调查局 (FBI)及澳大利亚、加拿大、英国和新西兰的网络安全机构发布《内存安全路线图指南》,点名C/C++存在内存安全漏洞,软件开发商应放弃使用,改用C#、Rust、Go、Java、Python和Swift等内存安全的编程语言 (MSL)。

那么,这究竟是什么情况,我们还能安心使用C/C++吗?

C/C++的黑暗面

内存安全漏洞(CWE-1399:综合分类:内存安全)是一类影响在编程语言中以意外方式访问、写入、分配或释放内存的漏洞。

透过漏洞,恶意行为者能够非法访问数据、损坏数据或运行任意恶意代码。例如,恶意行为者可能会向应用程序发送精心制作的有效载荷,从而破坏应用程序的内存,然后使其运行恶意软件。或者,恶意参与者可以发送包含恶意软件的格式错误的映像文件,以在受害者系统上创建交互式外壳。如果参与者可以以这种方式执行任意代码,参与者可以获得对运行该软件的帐户的控制权。

事实上,早在去年9月,微软CTO Mark Russinovichi就在其社交账号上发布动态称,开发人员是时候停止使用C/C++来启动新项目,建议在使用non-GC语言的场景中使用Rust。

2022年底,NSA也曾在报告《Software Memory Safety》中呼吁过放弃C/C++,彼时他们表示,C/C++已被证实是内存安全漏洞的温床,它们在内存管理方面提供了很大自由度和灵活性,用这种语言开发的应用程序安全性很大程度上需要依赖程序员的测试、检测环节,与此同时,他们还鼓励各组织将编程语言从C/C++转向C#、Rust、Go、Java或Ruby等编程语言。

今年1月,C/C++的问题甚至引起了拥有87年历史的《消费者报告》的关注,表明了对这个问题的认识,彼时报告中称,内存安全是复杂的话题,据估计,至少有65%的安全漏洞是内存错误的结果。

此次报告中则指出,这个几十年前就首次发现的缺陷仍然是当前恶意行为者经常利用的常见漏洞,以破坏应用程序和系统。比如说:

  • 微软在2019年一次会议上表示,自2006年至2018年,70%的漏洞是由内存安全问题引起的;
  • 在GoogleChromium项目中发现的漏洞中,约70%是内存安全漏洞;
  • 在对Mozilla漏洞的分析中,34个严重/高度错误中有32个是内存安全漏洞;
  • 根据Google Project Zero团队的分析,2021年有67%的零日漏洞是内存安全漏洞。

其实C/C++的这种漏洞并非不可控的,许多软件制造商投资于开发人员的培训计划。许多这些培训计划包括旨在减少由这些语言产生的内存不安全漏洞的流行率的策略。此外,还有许多商业和工业贸易协会的培训计划。此外,各种组织和大学提供培训和专业证书,以证明在C和C++安全编码实践的知识。

虽然培训可以减少编码员可能引入的漏洞数量,但考虑到内存安全缺陷的普遍性内存安全漏洞仍然会出现,这几乎是不可避免的。即使是最有经验的开发人员编写的bug也会引入显著的漏洞。培训应该是一个组织实现更健壮的技术控制(如内存安全语言)的桥梁。

许多公司针对这种漏洞,进行了许多优化。很多科技公司引入了代码覆盖测试、安全编码准则、Fuzzing 测试软件及开发者使用了静态应用程序安全测试(SAST)和动态应用程序安全测试(DAST)工具来查找各种软件的内存安全漏洞。比如说,C++社区一直在考虑向后兼容性、内存安全默认值和基础语言的其他优先级之间的平衡;苹果修改了iBoot system中使用的C编译器工具链,以缓解内存和键入安全问题;微软早些年还开源了一个更安全的C语言版本Checked C,在C中添加静态和动态检查,以检测或防止常见的编程错误;Google打造了一款 C++ 的继任者Carbon,针对现有代码的C++内存安全采取了改进措施。

硬件厂商,也在积极开发使用硬件支持内存保护。比如说,美国SRI International和剑桥大学的联合研究CHERI项目,为现有的芯片架构增加了新的功能;英国政府的数字安全设计(DSBD)计划汇集7000万英镑的政府资金和1.17亿英镑的工业联合投资开发技术;2022年,Arm公司探讨了其实验性Morello Program及CHERI架构实现原理,希望借此解决系统攻击中常被利用的一系列内存访问漏洞;而后,微软也参与了CHERI研发的相关工作。

当然,即便厂商已经投入大量心力避免漏洞,但结果依然不容乐观,而这正是CISA、NSA、FBI反驳使用C/C++的根本原因,而报告中,则推荐了C#、Rust、Go、Java、Python和Swift等几个编程语言。

C and C++

安全性不等于内存安全

面对市场传言,C++之父Bjarne Stroustrup并没有选择沉默,而是在今年多次回击。

其中,最具代表性的就是其在2月发布的《Think seriously about“safety”;then do something sensible about it》(认真考虑“安全”,然后采取一些明智的措施”)一篇文章。

文章中,Bjarne表示,很多人的焦点都放在C/C++的弱点上,但实际这些缺陷是完全可以避免的。首先,要声明的是,C和C++根本就是天壤之别的两种语言,请不要混为一谈。C++能更加直接的表达程序员的想法,NSA完全忽略了C++在30年依赖的进步,并将C与C++混为一谈,这让人深表遗憾。

C++正引入更多特性,变得越来越安全,合格的C++程序员可以写出安全性可以与Rust媲美的程序。忽视安全问题会伤害C++社区的大部分成员,并破坏我们为改进C++所做的许多其他工作。专注于安全也是如此。

除此之外,“安全”的定义并不只有一个,我们可以通过编程风格、库的组合以及静态分析来实现各种各样的安全。NSA对“安全”的概念仅限于内存安全,而忽略了一门语言可能被用来违反某种形式的安全保障和十几种其他方式。

不是每个人都把“安全”看得高于一切。比如,在性能是主要关注点的应用程序域中,规则中允许仅在需要应用时保证安全,并在需要时使用最喜欢的调优技术。

目前世界上有数百万C++程序员与数十亿行C++代码,被应用在包括航空航天、医疗仪器、人工智能/机器学习、知识图谱、生物医学及高能物理等领域。据我所知,在发布报告前,没有一位专家向C++标准委员会进行过咨询,所谓“明智的做法”是什么,建议列出一个明确的清单。

1月中旬,官方C++“指导小组”发布了一份声明,解决了人们对C++安全性的担忧。虽然许多语言现在都支持“基本类型安全”,即确保变量只访问由其数据类型明确定义的内存部分,但C++一直难以提供类似的保证。

工程师怎么看

大佬们吵翻天,工程师其实都早已看在眼里。“菜刀可能会导致剁掉手掌,请所有支持C++不安全观点的人,以后不要吃饭。”一位工程师发出这样的灵魂拷问,他把C++比喻成一把锋利的刀,高效好用,而CISA的说法,就像是说刀太快,不安全,要包上棉花使用。

“美国就是喜欢揪着小尾巴不放,但不用C/C++,操作系统、编译器、执行器、甚至虚拟机,都会完全无法工作?虽然C/C++的指针使用不当会导致一系列问题,但指针对内存的精确控制,也能从一定程度上节省内存消耗,这难道不是一个程序员更应该考虑的吗?”

C接近汇编,执行效率高是优点,但确实对技术架构和编程质量有较高要求,因为很容易实现底层控制。那时也少有分层分级管理的概念,不像现在,伴随着黑客的门槛下降,动不动就零信任。

有工程师表示,大家都对内存安全嗤之以鼻,但还有多少人对C++停留在98版本,还有多少人在使用int *p=new int这种语法?还在使用说明根本不了解现代C++,iterator 、智能指针都能代替传统的指针,很多问题诸如用户用自己定义的函数操纵了类受保护的变量,是编程语言也处理不了的,只能依靠程序员自己。明明是程序员自己编程犯的错,为什么要把责任推到C++语言头上?为什么不从积极的角度把C++的这种特性看成是灵活运用指针自由使用内存数据的语言能力呢?

“没有绝对安全的编程语言,更没有能写出绝对安全代码的程序员。我们总是要平衡利弊,找到那个平衡点。这也是计算机领域发展至今的铁律。”一位程序员这样总结道。

当然,也有程序员认为,编程语言的代谢速度到了一个临界点,迎接新的挑战才是常态。编写糟糕的代码是开发人员的过错,但令人惊讶的是,过了这么久才指出这一点,大多数人都不擅长编写良好的代码,或者说,因为能安全使用C和C++的年轻人越来越少了。所以,我们从使用的语言中得到的帮助越多越好。

作为IT行业的晴雨表,TIOBE编程语言排行榜向来最能反映当前编程语言的热度和变化趋势。2023年,12月份的TIOBE编程语言排行榜中显示,C与C++依然位列第二和第三,但从趋势上来看,C#的表现强劲,有着威胁C与C++的能力。作为竞争者,C#在1年内上涨了2.38%,C#相比Java,最大的优势在于可以非常高效用在工业界,对接大量已存在的C/C++代码,但Java就不行。

C and C++

“新的语言通常需要多年的时间和重大的努力,才能在其广泛的应用领域中与成熟的语言相媲美。发烧友们很少看到这一点,他们的评论往往是相当片面的。”Bjarne曾经在与Azure CTO一次隔空辩论中如是说。