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

安全基础
IE浏览器防黑十大秘籍,黑客也没招
网络工程师讲解系统安全漏洞的形成和防治
清除导致XP系统反复重启的新网银木马
识破QQ欺骗网络地址的几种方法汇总
安全基础知识 细说暴库的原理与方法
排除无线突然中断故障实例
强搜天线 搜出WiFi世界的安全漏洞
网管应用技巧 内网安全十大策略说明
如何修改局域网内部打印机的IP地址
如何找出IIS中隐藏的网站
EFS加密技术的概念分析及一次解密经过
提高Windows XP系统安全性要关闭的10种服务
PHPBB 2.0.22 MOD版最新注入漏洞
修复Windows系统忘记密码的9个高招
用SockOnline软件轻松突破端口限制
安全基础知识 最强0到33600端口详解
执行文件方式加密FLASH文件的解密方法
网吧被入侵后的应对解决方法
网页“黑手”如何攻击你的Windows系统
不要让别人读了你的信 谈私密数据保护

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


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-10-28   浏览: 35 ::
收藏到网摘: 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,都有内置的可以自动处理锁维护(并使结果有可能更正确)的语言结构。

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