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

发布于 2024-10-21 18:24:43 字数 820 浏览 1 评论 0原文

我有一个数字列表,希望为我的用户提供输入 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" );

在这个例子中,我知道我有四个浮点数。 我想允许任何仅引用零到四个浮点数的格式。

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

  • "%f %g %e %.1f"
  • "Foo is %g, Bar is %g"
  • "Nothing"

而以下应该是被拒绝:

  • “%s”
  • “Foo 是 %d”

I have a list of numbers and want to give my users the option to enter a printf-style format string to specify how the numbers should be output.

How can I validate the user-supplied format string against my parameter list? Malformed input should not crash the program, and I want to avoid any format string attacks.

I do not care if the validation handles just the format options specified in POSIX or the compiler specific superset. Is there any library call to do this, or will I have to write it myself?

Clarification:
What I need is something like this:

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" );

In this example, I know that I have four floats.
I want to allow any format that only references zero to four floats.

So the following format strings should be allowed by isValidFormat():

  • "%f %g %e %.1f"
  • "Foo is %g, Bar is %g"
  • "Nothing"

While the following should be rejected:

  • "%s"
  • "Foo is %d"

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

So尛奶瓶 2024-10-28 18:24:43

不要将用户输入的字符串传递到 printf 中。 printf 的格式字符串接口是为代码设计的,而不是为人类输入设计的。您会发现,如果您只是制定自己的格式字符串规范,您将可以自由地针对用户进行设计。

您是否有理由将完全混淆的 printf 格式字符串规范强加给您的用户?

本质上,您是在请求帮助编写一些内容来将格式字符串规范转换为 printf 格式字符串规范。相反,我建议您编写代码来根据用户输入的内容构造 printf 格式字符串。这样更安全,并为您提供更大的灵活性。即使代码更多,也不会那么麻烦。

Do not pass user-inputted strings into printf. printf's format string interface is designed for code, not for human input. You will find that if you just make your own format string spec, you will have the freedom to design it towards the users.

Is there a reason you want to impose the completely obfuscated printf format string spec on your users?

Essentially you are asking help writing something to convert your format string spec to the printf format string spec. I would instead suggest you write code to construct the printf format string from the one the user types in. This is safer, and gives you more flexibility. Even if it's more code, it's less hacky.

末蓝 2024-10-28 18:24:43

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

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

可能出现在格式说明符之间任何位置的文字文本的正则表达式将类似于:

([^%]|%%)*

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

Writing the code for you is too much work, but I'll give you a good approach. Design regular expressions for the valid format specifiers for each type you need to support, then use them to construct a larger regular expression for the entire format string and see if it matches. For example, a floating point (double) argument's regex would look something like:

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

And a regex for literal text that could appear anywhere between format specifiers would look something like:

([^%]|%%)*

Make sure when matching the format string you insist that the whole string match the regex (use ^ and $ anchors at the ends) and not just a substring.

梦回梦里 2024-10-28 18:24:43

没有标准(POSIX 或 C)方式,而且我知道没有库提供这种方式。所以你必须自己写或者做比我更好的搜索。请注意,您必须仅检查那些对您有用的内容。

There is no standard (POSIX or C) way and I know of no library providing this. So you will have to write your own or do a better search than me. Note that you have to check only those which are useful for your.

离去的眼神 2024-10-28 18:24:43

如果您有预定义的输入参数,请使用 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;
}

If you have pre-defined input parameters, use 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;
}
ˉ厌 2024-10-28 18:24:43

在 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);
}

In RRDtool I use code like this to check various format patterns.

#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);
}
时光磨忆 2024-10-28 18:24:43

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

The easiest would be to use sprintf (instead of printf) to compute the result in a string and the test the error code returned by sprintf.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文