以最安全的方式在C语言中打开文件

4
我将编写以下程序:
int main ()
{
      FILE *fp = fopen("test.txt", "r")

      if (fp == NULL)
      {
           printf("Sorry, file doesn't exist.");
           return 0;     
      }

   return 0;
} 

在打开文件之前或之后,我需要进行哪些其他检查?

如果打开文件会损坏系统(病毒),是否有相应的检查措施?

如果文件不是 .txt 文件,而是用户将 mp3 文件重命名为 txt 文件,该怎么办?


1
@cygwinnian 我想知道为什么面试官会问这样一个简单的问题。 - codey modey
4
您的错误信息可能会引导人产生误解。即使文件存在,但当前用户没有打开它的权限,它仍会显示该文件不存在的信息。更好的错误提示应该是:fprintf(stderr, "error: cannot open %s for reading: %s\n", "test.txt", strerror(errno)); - pts
1
如果你在出现致命错误时从 main 函数中 return 一个非零值(或使用非零的退出代码调用 exit),那么你的程序就会变得更加有用。 - pts
如果打开一个文件可能会损坏系统(病毒),有什么检查可以进行吗?如果文件不是 .txt 文件,而只是用户将 mp3 文件重命名为 txt 文件,会怎样? - codey modey
5个回答

3

不需要进行其他检查。

当使用 FILE* 结束后,我通常也会调用 fclose 函数。


如果打开文件可能会损坏系统(病毒),有没有检查的方法?如果文件不是 .txt 文件,用户只是将 mp3 文件重命名为 txt 文件,怎么办? - codey modey
如果是二进制文件,使用rb打开。r是用于文本文件的。我认为使用r调用fopen打开文件不会像病毒那样对系统造成损害。 - edtheprogrammerguy

2

只应该使用模式r打开文本文件。不同的系统以不同的方式存储文本,此模式将自动翻译文件。例如,DOS/Windows使用\r\n表示换行,类UNIX系统使用\n,MAC系统使用\r等其他可能存在的差异。

为了真正以原始的二进制安全模式打开和读取文件,您应该使用rb

此模式使您完全控制您要写入的二进制级别的内容,是读取和写入二进制数据(如struct转储、加密信息等)的唯一安全方式。


1

打开文件后

无论是打开、读取、写入还是关闭文件,都要像打开文件时一样小心谨慎。因此,始终检查与此相关的调用的结果。


1

我看到这是一个旧问题。但是:

您可以进行一些额外的安全检查,以验证您是否打开了一个真实的文件,而不是符号链接。

原因:将文件替换为指向另一个文件的链接是已知的攻击技术之一(与CWE-362相关)。该文件可以被指向包含应该黑客/崩溃您的应用程序的序列的经过工程化的文件的链接所替换,而不是预期的文件。

因此,最佳做法是添加以下代码:

   #include <sys/types.h>
   #include <sys/stat.h>
   #include <unistd.h>

    /* Return: 0 if file is a regular file, -1 if not */
    static int test_file_type(const char *file)
    {
        struct stat st;

        if (0 != lstat(file, &st)) {
            fprintf(stderr, "lstat failed, probably no such a file\n");
            perror("lstat");
            return (-1);
        }

        if (S_IFREG != (st.st_mode & S_IFMT)) {
            fprintf(stderr, "Error on file [%s] opening: not a regular file, but ", file);
            switch (st.st_mode & S_IFMT) {
            case S_IFSOCK:
                fprintf(stderr, "socket\n");
                break;
            case S_IFLNK:
                fprintf(stderr, "symbolic link\n");
                break;
            case S_IFBLK:
                fprintf(stderr, "block device\n");
                break;
            case S_IFDIR:
                fprintf(stderr, "directory\n");
                break;
            case S_IFCHR:
                fprintf(stderr, "character device\n");
                break;
            case S_IFIFO:
                fprintf(stderr, "FIFO\n");
                break;
            }
            return (-1);
        }
        return (0);
    }

如果这个函数返回 < 0,则该文件不是常规文件。不要打开它。
另外,请阅读此处

0

你可能更喜欢使用fopen_s。fopen_s是fopen的一个新变体,它具有验证参数,并在打开过程中出现问题时返回错误代码而不是指针的情况。它比基本变体更安全,因为它考虑了更多的边缘条件。

希望对你有所帮助!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接