当前位置: 首页 > 图文教程 > 网络安全 > 安全基础 > 安全编程: 避免竞争条件(2)

安全基础
Windows防非法用户入侵技巧
三招两式抵制IE顽固病毒
防御DDoS攻击的实时监测模型
简单分析Script脚本跨站攻击漏洞技术
网络安全专家支招防范黑客攻击九大方法
2007年度网络安全分析报告
如何让你的U盘做到100%预防电脑病毒
三招两式抵制IE的顽固病毒
黑客发起“瞬时攻击“的危险性
网上常见的网络陷阱 网友来看
2007网络安全焦点:终端Web安全
用十大安全策略加固无线局域网安全
小心美丽圣诞树的背后藏有恶毒的蛇
追踪网络攻击 简单方法查找黑客老巢
解读身份认证技术现实化
证券期货系统网络安全完美解决方案
最好的虚拟主机安全配置方法
Windows系统用户摆脱黑客攻击的方法
计算机病毒防治常遇问题
加固Wi-Fi无线网络安全的17招式(上)

安全基础 中的 安全编程: 避免竞争条件(2)


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-10-28   浏览: 36 ::
收藏到网摘: n/a

锁文件

  通常,类 Unix 系统是通过创建表示一个锁的文件来实现不同进程间共享的锁。使用单独的文件来表示锁,是“劝告式(advisory)”锁而不是“强制(mandatory)”锁的一个例子。换句话说,操作系统不会强制您通过锁来共享资源,所以,所有需要该资源的进程都必须协同使用该锁。这看起来好像很简单,但并不是所有简单的主意都不是好主意;创建单独的文件,就可以方便地获得系统的状态,其中包括哪些资源被加锁了。如果您使用这种方法,有一些标准的技巧可以简化这些锁的清除,具体地说,是删除那些挂起的锁。例如,一个父进程可以设置一个锁,然后调用一个子进程来执行工作(确保父进程可以有效地调用子进程),当子进程返回时,父进程释放该锁。或者,可以使用 cron 作业来查看那些锁(其中包括进程的 id);如果进程没有处于活动状态,那么该作业就会清除那些锁,并重新启动相应的进程。最后,锁文件的清除可以作为系统启动的一部分(从而使您的锁在系统突然崩溃之后不再处于挂起状态)。

  如果您正在创建单独的文件来表示锁,那么要注意一个常见的错误:对 creat() 或者与之相当的 open() 的调用(模式为 O_WRONLY | O_CREAT | O_TRUNC)。问题是,root 总是 可以这样创建文件,即便锁文件已经存在,这意味着该锁不能为 root 正常工作。简单的解决方案是在使用 open() 时指定标记 O_WRONLY | O_CREAT | O_EXCL(将权限设置为 0,使同一用户的其他进程无法获得该锁)。注意 O_EXCL 的使用,这是创建“专用”文件的正式途径;甚至在本地文件系统上,root 也可以这样做。这个简单的方法对 NFS 版本 1 或者版本 2 不适用;如果必须在使用这些老的 NFS 版本连接的远程系统上使用锁文件,那么可以使用 Linux 文档中给出的方案:“在相同的文件系统上创建一个惟一的文件(例如,结合主机名和 pid),使用 link(2) 来创建一个指向锁文件的链接,使用 stat(2) 来检查该惟一文件的链接计数器是否增加到了 2。不要使用 link(2) 调用的返回值。”

  如果您使用文件来表示锁,那么要确保这些锁文件放置在攻击者无法利用(例如,不能删除它们或者添加干扰它们的文件)的位置。典型的解决方案是使用一个目录,使该目录的权限根本不允许未经授权的程序添加或者删除文件。确保只有您可以信任的程序才能添加或者删除锁文件!

  文件系统层次结构标准(Filesystem Hierarchy Standard,FHS)得到了 Linux 系统的广泛使用,同时还引入了这类锁文件的标准约定。如果您只是希望确保您的服务器在一台给定的机器上运行不超过一次,那么您通常应该创建一个名为 /var/run/NAME.pid 的进程标识符,以进程 id 作为文件内容。根据同样的思路,您应该将设备锁文件之类的锁文件放置在 /var/lock 中。


锁文件的代替者

  使用单独的文件来表示锁是一个非常古老的方法。另一个方法是使用 POSIX 记录锁(record locks),它通过 fcntl(2) 实现为一个任意的锁。采用 POSIX 记录锁的理由有很多:POSIX 记录锁在几乎所有的类 Unix 平台上都获得了支持(它得到了 POSIX.1 的授权),它可以锁定文件的一部分(而不是只会锁定整个文件),而且它可以区别处理读锁和写锁的不同之处。此外,如果一个进程死掉,那么它的 POSIX 记录锁就会自动被删除。

  只有所有程序都共同合作的时候,使用单独的文件或者 fcntl(2) 任意锁才能生效。如果您不喜欢该思想,那么可以转而使用 System V 风格的强制锁。强制锁允许您锁定一个文件(或者它的一部分),使每一次 read(2) 和 write(2) 都检查锁,任何没有持有该锁的操作都将被挂起,直到该锁被释放为止。这样做可能稍微方便一些,但也有其缺点;拥有 root 特权的进程也可能被强制锁挂起,这样通常容易造成拒绝服务(denial-of-service)攻击。实际上,拒绝服务问题是非常严重的,因此通常要避免使用强制锁。强制锁是可用范围很广,但它不是通用的;Linux 和基于 System V 的系统支持这种锁,但其他的类 Unix 系统不支持它。在 Linux 上,为了启用强制文件锁,必须用特定的方式装配文件系统,因此很多配置在默认情况下不支持强制文件锁。

  在一个进程内部,线程可能也同样需要锁;有很多书都非常详细地讨论了这些问题。在这里,我们要讨论的主要问题是确保您小心地涵盖了所有情况;很容易忘记某个特定情形,或者没有正确处理。事实上,正确使用锁是很难的,攻击者可能利用这些锁处理中的错误。如果您需要在一个进程内部对线程使用很多锁,那么可以考虑使用自动完成锁的维护的语言或者语言结构。有很多语言,比如 Java 和 Ada95,都有内置的可以自动处理锁维护(并使结果有可能更正确)的语言结构。

  只要有可能,在开发程序时最好根本不使用锁。一个单独的服务器进程每次只接受一个客户机请求,然后处理该请求,直到完成该请求为止,而后再获得下一个请求,从某种意义上讲,进程内部的所有对象是被自动锁定的;这种简单的设计可以避免很多危险的加锁问题。如果您需要一个锁,那么保持其简单性(比如为几