📜  竞争条件漏洞

📅  最后修改于: 2021-08-24 16:48:17             🧑  作者: Mango

当多个线程读取和写入相同的变量时即发生竞争状态,即它们有权访问某些共享数据,并试图同时更改它。在这种情况下,线程彼此“竞争”以访问/更改数据。

这是一个主要的安全漏洞[CWE-362],通过操纵操作的时间可能会出现异常结果。此漏洞在TOCTOU(检查时间,使用时间)窗口期间出现。

在TOCTOU窗口期间的文件访问流程

在TOCTOU窗口期间的文件访问流程

因此,如果我们在此TOCTOU窗口本身中锁定文件怎么办!

  1. 一般的误解–
    解决此漏洞的一种简单方法可能是在此“检查和使用”窗口期间锁定文件本身,因为在此时间窗口内,没有其他进程可以使用该文件。

    看起来很简单,那为什么不实用呢?为什么我们不能使用这种方法来解决竞争条件问题?
    答案很简单,仅通过锁定文件并不能真正防止这种漏洞。

  2. 锁定文件时出现问题–
    仅当文件已经处于打开状态时,该文件才被锁定用于其他进程。此过程称为检查并打开过程,在此期间无法锁定文件。创建的任何锁都可以被攻击或恶意进程忽略。

    实际发生的情况是,对Open()的调用不会阻止对锁定文件的攻击。当该文件可用于检查和打开过程时,该文件实际上对任何访问/更改都是打开的。因此,目前无法锁定文件。这使得恶意进程几乎不存在任何类型的锁。

    在内部,它使用sleep_time,每次尝试都会加倍。通常将其称为自旋锁或繁忙的等待形式。此外,始终存在文件被无限期锁定的可能性,即被卡在死锁中的危险。

  3. 即使我们能够以某种方式锁定文件,该怎么办?
    让我们尝试锁定文件,看看可能有什么弊端。可用的最常见的锁定机制是原子文件锁定。通过使用锁文件在同一文件系统上创建唯一文件来完成此操作。我们使用link()来链接到锁文件,以便对文件进行任何形式的访问。
    • 如果link()返回0,则锁定成功。

    最常见的修复方法是将应用程序的PID存储在锁定文件中,并与当时的活动PID进行检查。然后,此修复程序的另一个缺点是ƒPID可能已被重用。

  4. 实际解决方案–
    更好的解决方案是,而不是在整个文件上创建锁定,而是将文件的各个部分锁定到不同的进程。

    例子 –
    当进程要写入文件时,它首先要求内核锁定该文件或文件的一部分。只要该进程保持锁定状态,其他任何进程都不会要求锁定文件的同一部分。因此,您可以看到并发问题正在像这样得到解决。

    以相同的方式,进程在读取文件内容之前要求锁定,这确保了只要保持锁定就不会进行任何更改。

    区分这些不同类型的锁是由系统本身完成的。该系统具有区分文件读取所需的锁和文件写入所需的锁的能力。这种锁定系统是通过flock()系统调用实现的。 Flock()调用可以具有不同的值:

    • LOCK_SH(读取锁定)
    • LOCK_EX(用于编写)
    • LOCK_UN(解除锁定)

    使用这些单独的调用,我们可以判断出需要哪种锁。

    这里要注意的一点是,由于没有人会尝试更改文件内容,因此许多过程可以同时受益于读取锁定。但是,只有一个进程可以从当前正在使用的给定时间的锁中受益。因此,即使读取也不能同时允许其他锁。

    这种精心设计的系统与可以要求内核在读取或写入重要的系统文件之前保留其访问权限(其锁)的应用程序很好地结合在一起。因此,这种有选择地锁定文件的方法比我们最初的方法非常实用。因此,当您尝试为目录实现自己的文件系统时,可以利用此安全编码技术来防止潜在的CWE-362(种族条件漏洞)。