如何根据参数列表验证用户提供的printf格式字符串?

时间:2011-03-14 16:20:04

标签: c validation format printf

我有一个数字列表,并希望为我的用户提供输入printf样式格式字符串的选项,以指定数字的输出方式。

如何根据参数列表验证用户提供的格式字符串?格式错误的输入不应该使程序崩溃,我想避免任何格式字符串攻击。

我不关心验证是否仅处理POSIX或编译器特定超集中指定的格式选项。是否有任何图书馆电话要求,或者我必须自己写吗?

澄清: 我需要的是这样的事情:

float var1, var2, var3, var4;
// var1 .. var2 are given by the program
const char * userSupplied = getFormatStringFromUser();

if( isValidFormat( userSupplied, "float", "float", "float", "float" ) )
    printf( userSupplied, var1, var2, var3, var4 );
else
    printf( "The format you gave was invalid!\n" );

在这个例子中,我知道我有四个花车。 我想允许任何只引用0到4个浮点数的格式。

因此 isValidFormat()应允许以下格式字符串:

  • “%f%g%e%.1f”
  • “Foo是%g,Bar是%g”
  • “无”

虽然应拒绝以下内容:

  • “%S”
  • “Foo is%d”

6 个答案:

答案 0 :(得分:1)

不要将用户输入的字符串传递给printfprintf的格式字符串接口是为代码而设计的,不是为人类输入而设计的。你会发现如果你只是制作自己的格式字符串规范,你就可以自由地为用户设计它。

您是否有理由对用户强制使用完全混淆的printf格式字符串规范?

基本上,您正在请求帮助编写内容以将格式字符串规范转换为printf格式字符串规范。我建议你从用户键入的构造 printf格式字符串中编写代码。这样更安全,并为您提供更大的灵活性。即使它的代码更多,也不那么苛刻。

答案 1 :(得分:1)

为你编写代码太多了,但我会给你一个很好的方法。为您需要支持的每种类型设计有效格式说明符的正则表达式,然后使用它们为整个格式字符串构造一个更大的正则表达式,并查看它是否匹配。例如,浮点(double)参数的正则表达式看起来像:

%[+- 0#]*[0-9]*([.][0-9]+)?[aefgAEFG]

可以出现在格式说明符之间的文字文本的正则表达式如下所示:

([^%]|%%)*

确保在匹配格式字符串时,您坚持整个字符串与正则表达式匹配(在末尾使用^$锚点)而不仅仅是子字符串。

答案 2 :(得分:0)

没有标准(POSIX或C)方式,我知道没有提供此功能的库。因此,您必须自己编写或搜索比我更好的搜索。请注意,您只需要检查对您有用的那些。

答案 3 :(得分:0)

如果您有预定义的输入参数,请使用switch case。

switch ( <variable> ) {
case accetable-value_1:
  Code to execute if <variable> == accetable-value_1
  break;
case accetable-value_2:
  Code to execute if <variable> == accetable-value_2
  break;
...
default:
  error: This is not a valid value, please enter a valid value
  break;
}

答案 4 :(得分:0)

在RRDtool中,我使用这样的代码来检查各种格式模式。

#include <glib.h>

static int bad_format_check(const char *pattern, char *fmt) {
    GError *gerr = NULL;
    GRegex *re = g_regex_new(pattern, G_REGEX_EXTENDED, 0, &gerr);
    GMatchInfo *mi;
    if (gerr != NULL) {
        // rrd_set_error("cannot compile regular expression: %s (%s)", gerr->message,pattern);
        return 1;
    }
    int m = g_regex_match(re, fmt, 0, &mi);
    g_match_info_free (mi);
    g_regex_unref(re);
    if (!m) {
        // rrd_set_error("invalid format string '%s' (should match '%s')",fmt,pattern);
        return 1;
    }
    return 0;
}

#define SAFE_STRING "(?:[^%]+|%%)*"

int bad_format_imginfo(char *fmt){
    return bad_format_check("^" SAFE_STRING "%s" SAFE_STRING "%lu" SAFE_STRING "%lu" SAFE_STRING "$",fmt);
}
#define FLOAT_STRING "%[-+ 0#]?[0-9]*(?:[.][0-9]+)?l[eEfF]"

int bad_format_axis(char *fmt){
    return bad_format_check("^" SAFE_STRING FLOAT_STRING SAFE_STRING "$",fmt);
}

int bad_format_print(char *fmt){
    return bad_format_check("^" SAFE_STRING FLOAT_STRING SAFE_STRING "%s" SAFE_STRING "$",fmt);
}

答案 5 :(得分:-2)

最简单的方法是使用sprintf(而不是printf)来计算字符串中的结果,并测试sprintf返回的错误代码。