使用 fgets() 和推荐的解决方案可能存在安全漏洞?

Possible security vulnerability from using fgets() and recommended solution?

我正在使用 coverity 的 SA 工具来解决错误。由于使用 fgets(),我遇到了一些错误。这是一个片段(SA 错误显示为注释)-

FILE *fp;
char my_pubkey[1024];

fp = fopen("publickey.pub", "r");

//tainted_string_argument: fgets taints variable my_pubkey.
if (!fgets(my_pubkey, sizeof(my_pubkey), fp)) {
    printf("failure to read pub key file");
    goto error;
}

//tainted_string: Passing tainted string my_pubkey to a parameter that cannot accept a tainted format string.
if (fprintf(fp, my_pubkey) != strlen(my_pubkey)) {
    printf ("failure to write pub key in key file");
    goto error; 
}

在我的调查中,一些报告建议改用 getline(),但这真的有必要吗?如果这是一个有效的问题,那么漏洞可能是什么?最好的解决方案是什么?

编辑:如果这是误报,为什么会这样?什么时候可以作为实际问题的示例?

假设键是最多 1024 个可打印字符的文本,那么代码应该使用 char my_pubkey[1024+ 2];\n[=13=] 生成 space。

如果密钥是二进制的,则以文本模式打开是错误的,使用 fgets() 会失败,因为它读取 。最好使用 fgetc()。也不要使用 strlen() 因为代码不处理 strings.

此外,内部 缓冲区长度应最小化以减少陈旧缓冲区中浮动的密钥副本。参见 setbuf()setvbuf()


fprintf() 将编码的字符串解释为格式说明符字符串。% 的存在引入了潜在的 UB。最好使用 %s.

if (fprintf(fp, "%s", my_pubkey) ...

Coverity 似乎在抱怨 my_pubkey 正在从外部来源接收值。因此它是 "tainted",因为程序本身不能确信如此接收的数据是正确或有效的。这是您真正需要处理的问题。我不希望使用 getline() 而不是 fgets() 来改变它——这将是解决涉及不同功能的不同问题的一种方法 (gets()).

Coverity 抱怨您将受污染的字符串作为格式字符串传递给printf()。这也是一个 善意的 安全问题,甚至可能是一个简单的正确功能问题。使用外部提供的字符串作为 [f]printf() 格式字符串是一个非常糟糕的主意,因为这样的字符串可能包含 printf() 字段代码。您应该改为提供明确的格式:

fprintf(fp, "%s", my_pubkey)

或使用fputs():

fputs(my_pubkey, fp)