密码存在内存里安全吗?

简介: 我刚刚意识到在任何语言里,当你把密码保存到一个变量里,它将以文本的形式存放在内存里。我认为操作系统将发挥作用,禁止进程相互访问彼此分配的内存。但是我也认为这多少有些不是靠谱的做法。因此我想知道这是不是真正安全的,是否有更加安全的存储密码的方法,来确保外部进程不能访问它们。

问:

我刚刚意识到在任何语言里,当你把密码保存到一个变量里,它将以文本的形式存放在内存里。


我认为操作系统将发挥作用,禁止进程相互访问彼此分配的内存。但是我也认为这多少有些不是靠谱的做法。因此我想知道这是不是真正安全的,是否有更加安全的存储密码的方法,来确保外部进程不能访问它们。


我没有指定操作系统或者语言,因为我的问题是非常普遍的。这更像是一个计算机常识问题、而不是具体的问题。


答:

你点到了一个痛处……

从历史上讲,计算机是大型机,允许许多不同的用户在相同的物理机上建立会话和进程。类Unix系统(比如Linux),还有VMS和其相关亲戚(和包括所有NT、和此后的2000,XP, Vista, 7, 8…… 的Windows家族,),设计之初是为了支持大型机模型。这样,硬件提供权限级别。操作系统最重要的部分是内核,它以最高权限级别运行(是的,我知道对于虚拟化有些微妙),并管理权限级别。应用程序以较低级别运行,内核禁止它读、写其他应用程序的内存。应用程序按页(page,通常4或8KB)从内核获得RAM。如果一个应用程序试图访问另一个应用程序的页,内核就会阻止它,并且受到严重惩罚(“segmentation fault”, “general protection fault”)。


当一个应用程序不再需要页(特别是在该应用程序还存在的时候)时,内核会控制该页,可能会分给另一个应用程序。现代操作系统在使用这些页的时候,会“清空”(blank)该页,这里的“清空”指“用零填充”,从而阻止数据从一个进程泄露到另一个进程。注意,Windows 95/98/Millenium没有清空页,泄露有时候会出现……但是,这些操作系统是针对单台机器的单个用户的。


当然,有很多绕过内核的方法:应用程序有一些方式来获取“足够的权限”(没有上面权限高的、同种类型权限)。Linux系统有ptrace,内核允许一个进程通过ptrace()读写另一个进程的内存,此时这两个进程需要运行在同一个用户ID下,或者运行ptrace()进程的是“root”用户。相似的功能也存在于Windows。


底线是RAM里的密码没有操作系统允许的安全。按照定义,在进程内存里存储一些机密数据有个前提,就是你相信操作系统不会把数据给第三方。操作系统是你的朋友,因为如果它是敌人,那么你就失去了一切。


有意思的环节到了。既然操作系统负责进程的隔离,还是有很多人试着找到了穿透防护的办法。这里是他们找到的有趣的办法……


应用程序看到的“RAM”不是真正需要的“内存”。内核是假象的管理者,分配实际不存在的页。假象是通过通过磁盘上的专用空间来交换RAM内容,磁盘上的空闲空间是相当大的;这被称为虚拟内存。应用程序无需关心,因为内核会根据需要取回这些页(当然,磁盘比RAM慢很多)。不幸的后果是,一些数据,特别是位于RAM中的数据,使得RAM变成 数据被覆盖之前的 物理载体。尤其是当电源被切断时,它仍然呆在那里。当坏家伙搞走这台机器,随后就能获取数据了。或者机器退役后在eBay卖出,系统管理员忘记了彻底清除磁盘内容。


Linux提供了一个mlock的系统调用来阻止内核把某些特定的页发送到swap区。既然锁住RAM里的页能够大大减少其他进程获取RAM资源的情况,那么你需要一些权限(还是root)来使用这个功能。


恼人的地方在于,追踪真正保存在RAM里的密码必然是不太容易的。做为一个程序员,你是通过编程语言提供的抽象方式来访问RAM的。尤其是使用垃圾回收的编程语言会明显地复制RAM里的对象(因为它对很多GC算法有帮助)。大多数编程语言就是这样被影响的(像Java, C#/.NET, Javascript, PHP……清单几乎没有尽头)。


hibernation似乎为了复仇带来了同样的问题。本来,hibernation必须把整个RAM写回磁盘——包括mlock()涉及的页,甚至CPU注册的内容。为了避免hibernation泄露数据,你不得不采取像加密整个磁盘之类的猛烈的措施——这自然意味着只要你唤醒机器,就可以键入解除锁定的密码。


大型机模型假定它能够运行一些彼此友好的进程,且保持极好的和平和隔离。现代硬件加剧了这一难度。当两个进程运行在同一个CPU时,它们共享资源,包括缓存内存;内存访问比缓存之外的任何地方都要快,但是缓存大小十分有限。这催生了一个进程使用来自于另一个进程的恢复密钥。使用其他类缓存的资源变体也有了,比如CPU里的分支预测(branch prediction)。随着对高度机密的、集中于密钥的研究,它能够真正应用于任何数据。


还有种情况,显卡能够做直接内存存取(Direct Memory Access,DMA)【注1】。DMA是否不会被滥用去读写其他进程的内存 取决于 文档不全的硬件、闭源的驱动程序和内核是如何协作确保合适的访问控制的。我不会为此赌我一件上次穿过的衬衣的……

结论:是的,当你在RAM存放密码时,你是相信操作系统会确保机密的。是的,这个任务很艰巨,甚至在现代系统上是不可能的。如果你的数据高度机密,你就不应该使用大型机模型,也不要允许潜在友好的实体在你的机器上运行代码。(顺便说一句,这意味着托管的虚拟主机和云计算根本是不安全的。如果你对安全比较在意,请使用专有硬件。)


注1:DMA:http://zh.wikipedia.org/zh-cn/%E7%9B%B4%E6%8E%A5%E8%A8%98%E6%86%B6%E9%AB%94%E5%AD%98%E5%8F%96

相关文章
|
Rust 安全 编译器
Rust中的生命周期与借用检查器:内存安全的守护神
本文深入探讨了Rust编程语言中生命周期与借用检查器的概念及其工作原理。Rust通过这些机制,在编译时确保了内存安全,避免了数据竞争和悬挂指针等常见问题。我们将详细解释生命周期如何管理数据的存活期,以及借用检查器如何确保数据的独占或共享访问,从而在不牺牲性能的前提下,为开发者提供了强大的内存安全保障。
|
存储 网络虚拟化 索引
【OSTEP】分页(Paging) | 页表中究竟有什么 | 页表存在哪 | 内存追踪
【OSTEP】分页(Paging) | 页表中究竟有什么 | 页表存在哪 | 内存追踪
533 0
|
安全 Java API
【性能与安全的双重飞跃】JDK 22外部函数与内存API:JNI的继任者,引领Java新潮流!
【9月更文挑战第7天】JDK 22外部函数与内存API的发布,标志着Java在性能与安全性方面实现了双重飞跃。作为JNI的继任者,这一新特性不仅简化了Java与本地代码的交互过程,还提升了程序的性能和安全性。我们有理由相信,在外部函数与内存API的引领下,Java将开启一个全新的编程时代,为开发者们带来更加高效、更加安全的编程体验。让我们共同期待Java在未来的辉煌成就!
237 11
|
安全 Java API
【本地与Java无缝对接】JDK 22外部函数和内存API:JNI终结者,性能与安全双提升!
【9月更文挑战第6天】JDK 22的外部函数和内存API无疑是Java编程语言发展史上的一个重要里程碑。它不仅解决了JNI的诸多局限和挑战,还为Java与本地代码的互操作提供了更加高效、安全和简洁的解决方案。随着FFM API的逐渐成熟和完善,我们有理由相信,Java将在更多领域展现出其强大的生命力和竞争力。让我们共同期待Java编程新纪元的到来!
439 11
|
11月前
|
SQL 存储 Java
关于内存安全问题,你应该了解的几点!
关于内存安全问题,你应该了解的几点!
110 0
|
数据采集 Rust 安全
Rust在网络爬虫中的应用与实践:探索内存安全与并发处理的奥秘
【8月更文挑战第31天】网络爬虫是自动化程序,用于从互联网抓取数据。随着互联网的发展,构建高效、安全的爬虫成为热点。Rust语言凭借内存安全和高性能特点,在此领域展现出巨大潜力。本文探讨Rust如何通过所有权、借用及生命周期机制保障内存安全;利用`async/await`模型和`tokio`运行时处理并发请求;借助WebAssembly技术处理动态内容;并使用`reqwest`和`js-sys`库解析CSS和JavaScript,确保代码的安全性和可维护性。未来,Rust将在网络爬虫领域扮演更重要角色。
217 1
|
Rust 安全 程序员
揭秘Rust语言的内存安全秘籍:如何构建坚不可摧的系统级应用?
【8月更文挑战第31天】Rust语言凭借其独特内存安全机制在编程领域脱颖而出,通过所有权、借用与生命周期等概念,在保证高性能的同时避免了缓冲区溢出等常见错误。本文深入探讨Rust的内存安全机制,并通过示例代码展示如何利用这些机制构建高效且可靠的系统。尽管这些机制增加了学习难度,但为软件开发奠定了坚实基础,使Rust成为系统、嵌入式及网络编程的理想选择。随着社区的发展,Rust将在未来软件开发中扮演更重要角色。
331 0
|
Rust 安全 开发者
探索Rust语言的内存安全特性
【6月更文挑战第8天】Rust语言针对内存安全问题提供了创新解决方案,包括所有权系统、借用规则和生命周期参数。所有权系统确保值与其所有者绑定,防止内存泄漏;借用规则保证同一时间只有一个可变引用或多个不可变引用,消除数据竞争和野指针;生命周期参数则强化了引用的有效范围,提升安全性。通过这些特性,Rust帮助开发者编写出更健壮、安全的高性能软件,有望成为系统编程领域的领头羊。
|
监控 Rust 安全
Rust代码在公司电脑监控软件中的内存安全监控
使用 Rust 语言开发的内存安全监控软件在企业中日益重要,尤其对于高安全稳定性的系统。文中展示了如何用 Rust 监控内存使用:通过获取向量长度和内存大小来防止泄漏和溢出。此外,代码示例还演示了利用 reqwest 库自动将监控数据提交至公司网站进行实时分析,以保证系统的稳定和安全。
613 2
|
Rust 安全 测试技术
使用Rust进行内存安全系统编程
【5月更文挑战第31天】Rust是一种保证内存安全的系统编程语言,通过所有权和借用系统防止内存错误,如内存泄漏和数据竞争。它的高性能、并发安全和跨平台特性使其在系统编程中占有一席之地。学习Rust涉及理解基本语法、所有权系统及使用标准库。通过案例分析,展示了如何在内存安全的前提下编写文件服务器。随着Rust的成熟,它在系统编程领域的应用前景广阔。