避免在统计数据和重命名之间进行TOCTOU(检查时间,使用时间)

Avoid TOCTOU (time-of-check, time-of-use) race condition between stat and rename

本文关键字:时间 检查 TOCTOU 统计数据 重命名 之间      更新时间:2023-10-16

如何避免toctou(检查时间,使用时间(的比赛条件的统计条件和重命名的logfile的竞赛条件?

在日志文件的尺寸值超过最大大小后,需要移动日志文件。

result = stat(LOGFILE, & data);
if (result != 0) {
  // stat failed
  // file probably does not exist
} else if (data.st_size > MAX_LOGSIZE) {
  unlink(PREV_LOGFILE);
  (void) rename(LOGFILE, PREV_LOGFILE);
}

避免使用文件操作的标准方法是 open a file 一次,然后通过文件描述符而不是文件名来完成所需的一切。

但是,重命名和未链接文件都需要其路径(因为他们需要知道要重命名或删除的链接(,因此您无法在此处使用该方法。一种替代方法可能是将文件的内容在其他地方复制,然后将其截断为零字节,尽管您使用日志文件的场景可能要求操作是原子,这可能很难实现。另一种方法是需要在目录上进行紧密的访问控件:如果攻击者无法写入目录,那么它就无法使用您的流程玩Tocttou游戏。您可以使用unlinkatrenameat将路径限制到特定目录的文件描述符,因此您不必担心目录本身会更改。

假设一个类似于Posix的平台的未经测试的代码可能会完成工作:

dirfd = open(LOGDIR, O_DIRECTORY);
// check for failure
res = fstatat(dirfd, LOGFILE, statbuf, AT_SYMLINK_NOFOLLOW);
if ((0 == res) && (S_ISREG(statbuf) && (data.st_size > MAX_LOGSIZE)) {
    unlinkat(dirfd, PREV_LOGFILE, 0);
    renameat(dirfd, LOGFILE, dirfd, PREV_LOGFILE);
}
close(dirfd);