如果一个程序能够长时间地充分地利用某些高速缓存或内存,我们就称这部分内存空间为持久内存。
内存和高速缓存一直是黑客攻击的重点领域之一,程序开发人员必须重视安全管理内存和高速缓存的策略问题。
限制持久利用高速缓存
当今程序员编制的许多软件都能持久而充分地利用高速缓存,从而实现软件性能的最大化。持久利用高速缓存的问题在于,数据在内存中的保留时间越长,如果故障导致存储器的信息转储,或者造成内存的内容可被直接访问,那么,数据被有意或无意泄露的可能性就越大。产生这个问题在原因在于,内存并不受操作系统级文件系统的访问控制的保护。
例如,当在Java企业版平台上使用Entity Bean时,数据可被存储到服务器上,并拥有容器级的持久性或bean级的持久性。无论是哪种情况,处理数据并将数据放到高速缓存的程序都需要提供安全的高速缓存管理功能,能够适应程序必须同时处理的所有进程(即接收并处理请求、会话管理、从数据库或文件系统读取数据)。开发人员可以在每次完成业务后,强制要求一个程序使用EJB来将数据写到持久的非易失存储器上,而不是将数据长久地放在内存中;此问题的平衡点在于性能(很少的数据库访问意味着更好的性能)与安全(很小的内存持久性意味着攻击者几乎没有机会访问没有被文件系统或数据库访问控制所保护的敏感数据)。在一个多用户的程序中,如果要求安全地管理持久使用的高速缓存,其成本和代价也会受到同时访问程序的用户数量的影响;被放到高速缓存的数据量越大,其它进程可用的内存量就越低。
在编制一个持久使用内存的程序时,程序员应当确保其持久度是可配置的,以便于管理员或用户可以根据需要而经常进行清除。理想情况下,程序还可以提供一个命令,允许管理员或用户随意清除内存。不管程序运行时的内存持久度如何,在程序关闭时都应当从内存中清除数据。对于极重要的可信任程序,程序不应当保留进程完成之后的内存空间,在进程完成时,程序应当清除内存。
理想情况下,极端敏感的数据,如认证令牌和加密令牌绝对不能放在持久内存中。但是,持久内存不可避免存在于商业化成品软件(包括开源软件)组件中。在这些情况下,如果组件有可能将敏感数据存放到持久内存中,开发者应当利用操作系统的高速缓存管理和对象重用功能。而且如果有的话,开发人员还应当利用数据库管理系统来覆盖每一个持久的内存位置。在利用商业化成品软件组件时,如果它使用持久内存,且它放到内存中的数据有可能是敏感数据,就应当把这个组件放到一个可信的处理器模块上,从而将其持久内存与系统的其它部分分离开来。
谨慎分配内存和其它资源
程序员应当使每个进程占用的计算机资源最小化。例如,对于运行在Unix上的软件而言,应使用ulimit()、getrlimit()、 getrusage()、sysconf()、 quota()、 quotactl()、quotaon()等,用以限制在特定进程发生故障或遭到破坏后导致的潜在损失,并防止对软件的拒绝服务攻击。
如果软件是一个Web服务、应用程序或Web服务,开发人员应当建立一个独立的过程,用以处理每次会话,并限制每个过程中每次会话被允许的CPU时间量。这样做可以防止任何攻击者过度使用内存或CPU周期的请求,防止他干扰其它任务。这样做可以使攻击者难以通过产生多个会话而制造拒绝服务攻击。
高速缓存缓冲区的内存位置不应当与可执行的堆栈部分相连。只要有可能,堆栈就应当是不可执行的。数据执行保护(DEP)是Windows操作系统的一个安全特性,这是可以确保可写内存无法被执行的一个简单方法。数据执行保护可以阻止被病毒或其它恶意代码感染的恶意程序,防止其运行被注入的代码。在一个支持DEP的系统上,执行被注入的代码会导致例外。强化DEP有助于阻止利用Windows的例外处理机制的恶意程序。
在返回库函数(return-to-libc)攻击中,程序的返回地址会被覆盖,用以执行攻击者想运行的代码,因而应当将系统代码移动到内存中的不同点上。地址空间的布局随机化(ASLR)是Windows操作系统的另一个安全特性,它可以在系统每次启动时将可执行的代码、库和代码的其它重要部分迁移到内存中的随机点上。这种特性可以使攻击者在利用代码时更难以提前做出预测。