未初始化的价值是否会带来安全风险?

在学习C的过程中,我犯了一些错误,并打印出未初始化的字符数组元素。

如果我将数组的大小扩展为相当大,比如说大小为100万个元素然后打印内容,那么出来的并不总是用户不可读,但似乎包含一些运行时信息。

请考虑以下代码:

#include  main() { char s[1000000]; int c, i; printf("Enter input string:\n"); for (i = 0; ( c = getchar()) != '\n'; i++) { s[i] = c; } printf("Contents of input string:\n"); for (i = 0; i < 999999; i++) { putchar(s[i]); } printf("\n"); return 0; } 

只需滚动输出,我就会发现如下内容:

?? ?L’ ?????? _ dyldVersionNumber_dyldVersionString_dyld_all_image_infos_dyld_fatal_error_dyld_shared_cache_ranges_error_string__mh_dylinker_header_stub_binding_helper_dyld_func_lookup_offset_to_dyld_all_image_infos__dyld_start__ZN13dyldbootstrapL30randomizeExecutableLoadAddressEPK12macho_headerPPKcPm__ZN13dyldbootstrap5startEPK12macho_headeriPPKcl__ZN4dyldL17setNewProgramVarsERK11ProgramVars__ZN4dyld17getExecutablePathEv__ZN4dyld22mainExecutablePreboundEv__ZN4dyld14mainExecutableEv__ZN4dyld21findImageByMachHeaderEPK11mach_header__ZN4dyld26findImageContainingAddressEPKv

并且,

Apple Inc.1&0 $ U?0?*?H ?? CA0?“0ple Certification Authority10U?GP ?? GP ?? ^ y? – ?6?WLU ???? Kl ??”0?>?P? A ?????????????????????????????????????????????????????????????????????????????????? ˬ,op ?? 0 ?? C ?? =?+ I(??ε?? ^ ?? =?:???? b ?? q?GSU?/ A ???? p ?? LE~ LKP?A 11 TB
?!?t?<?A?3 ??? 0X?Z2?h?es?g?e?I?v?3e?w ?? – ?? z0?v0U?0U?0?0U +?iG ?v ?? k?。@ ?? GM ^ 0U#0?+?iG?v ?? k?。@ ?? GM ^ 0?U 0?0? ?H ?? cd0 ?? 0+ https: //www.apple.com/appleca/0?+0????对任何一方对此证书的认可均接受当时适用的标准使用条款和条件,证书poli?\ 6?Lx?팛?? w ?? v?w0O ???? = G7?@?,Ա?s?s?d?yO4>?x?k ??} 9 ?? S? 8??? O 01 ?H ?? [d?c3w?:, V ??!s o ?? 6?U7 ?? 2B ??? q?〜?R ?? B $ * ?? M?^ c?K 2 P ???????? 7?UU!0?0?0

我相信有一次我的$PATH环境变量甚至打印出来了。

未初始化变量的内容是否会带来安全风险?

更新1

动力

更新2

所以从答案中可以清楚地看出,这确实是一种安全风险。 这让我感到惊讶。

除了初始化该内存的程序之外,程序是否无法声明其内存内容受到保护以允许操作系统限制对它的任何访问?

大多数C程序使用malloc来分配内存。 一个常见的误解是malloc将返回的内存归零。 它实际上没有。
结果,由于存储器块被“回收”的事实,很可能得到具有“值”信息的存储器块。
此漏洞的一个示例是Solaris上的tar程序,它发出了/etc/passwd 。 根本原因是分配给tar从磁盘读取块的内存未初始化,并且在获取此内存块之前, tar实用程序调用OS系统调用/etc/passwd 。 由于内存回收和tar没有初始化的事实, /etc/passwd的块片段被打印到日志中。 这是通过用calloc替换malloc来解决的。
如果您没有明确且正确地初始化内存,这是安全隐含的实际示例。
所以是的,请正确初始化你的记忆。

更新:

除了初始化该内存的程序之外,程序是否无法声明其内存内容受到保护以允许操作系统限制对它的任何访问?

答案是肯定的(见最后),没有。
我认为你在这里看错了。 更合适的问题是,例如,为什么malloc不会根据请求初始化内存或在发布时清除内存,而是回收它?
答案是API的设计者明确决定不初始化(或清除内存),因为对大型内存块执行此操作1)会影响性能,2)并不总是必要的(例如,您可能无法在应用程序中处理)或者您的应用程序中的几个部分,如果它们被暴露,您实际关注的数据)。 因此,设计师决定不这样做,因为它会无意中影响性能,并将球交给程序员来决定。
因此,对于操作系统而言,为什么操作系统有责任清除页面? 您期望从您的操作系统及时交出内存,但安全性取决于程序员。

虽然已经提到了一些机制,你可以使用它来确保敏感数据不会在Linux中使用mlock存储在交换中。

mlock()和mlockall()分别将部分或全部调用进程的虚拟地址空间锁定到RAM中,防止将内存分页到交换区域。 munlock()和munlockall()执行相反操作,分别解锁部分或全部调用进程的虚拟地址空间,以便在内核内存管理器需要时,可以再次交换指定虚拟地址范围内的页面。 内存锁定和解锁以整页为单位执行。

是的,至少在可以将数据传输给外部用户的系统上。

在网络服务器(甚至是iPod)上有一系列的攻击,你可以从其他进程转储内存的内容 – 所以获得操作系统的类型和版本,其他应用程序中的数据甚至是东西的详细信息像密码表

很可能在内存区域执行一些敏感工作,而不清楚缓冲区。

然后,未来的调用可以通过调用malloc()或通过检查堆(通过单元化的缓冲区/数组声明)来检索未清除的工作。 它可以(恶意地)检查它或无意中复制它。 如果您正在做任何敏感的事情,那么在对它进行分箱( memset()或类似)之前清除该内存是有意义的,并且可能在使用/复制它之前。

从C标准:

6.7.8初始化

“如果没有显式初始化具有自动存储持续时间的对象,则其值是不确定的。”

不确定值定义为:

  either an unspecified value or a trap representation. 

陷阱表示定义为:

某些对象表示不需要表示对象类型的值。 如果对象的存储值具有这样的表示,并且由不具有字符类型的左值表达式读取,则行为未定义。 如果这样的表示是由副作用产生的,该副作用通过不具有字符类型的左值表达式来修改对象的全部或任何部分,则行为是未定义的.41)这种表示被称为陷阱表示。

访问此类值会导致未定义的行为,并可能造成安全威胁。

本文攻击未初始化的变量可以给出一些可以用来利用系统的见解。

如果您担心安全性,最安全的方法是始终初始化您将要使用的每个变量。 它甚至可以帮助你找到一些错误。 没有初始化内存可能有一些很好的理由,但在大多数情况下初始化每个变量/内存将是一件好事。