C 哪个sprintf/snprintf更安全?

C 哪个sprintf/snprintf更安全?,c,security,unix,printf,secure-coding,C,Security,Unix,Printf,Secure Coding,我想知道这两个选项中哪一个更安全: #define MAXLEN 255 char buff[MAXLEN + 1] sprintf(buff,'%s',MAXLEN,name) snprintf(buff,MAXLEN,“%s”,name) 我的理解是两者是相同的。请提出建议。两者都会给出您想要的结果,但是snprintf更通用,并且无论给定的字符串格式如何,都会保护您的字符串不受溢出的影响 此外,由于snprintf(或sprintf)添加了一个最终的\0,因此您应该将字符串缓冲区增大一个

我想知道这两个选项中哪一个更安全:

#define MAXLEN 255
char buff[MAXLEN + 1]
  • sprintf(buff,'%s',MAXLEN,name)

  • snprintf(buff,MAXLEN,“%s”,name)


  • 我的理解是两者是相同的。请提出建议。

    两者都会给出您想要的结果,但是
    snprintf
    更通用,并且无论给定的字符串格式如何,都会保护您的字符串不受溢出的影响


    此外,由于
    snprintf
    (或
    sprintf
    )添加了一个最终的
    \0
    ,因此您应该将字符串缓冲区增大一个字节,
    char buff[MAXLEN+1]
    您给出的两个表达式并不等价:
    sprintf
    不接受指定要写入的最大字节数的参数;它只需要一个目标缓冲区、一个格式字符串和一组参数。因此,它可能会写入超过缓冲区空间的字节,并在这样做时写入任意代码。
    %.*s
    不是令人满意的解决方案,因为:

  • 当格式说明符指的是长度时,它指的是strlen的等价物;这是字符串中字符数的度量,而不是其在内存中的长度(即,它不计算空终止符)
  • 格式字符串中的任何更改(例如添加换行符)都将改变
    sprintf
    版本在缓冲区溢出方面的行为。使用
    snprintf
    ,无论格式字符串或输入类型如何更改,都会设置一个固定的清除最大值

  • 您的sprintf声明是正确的,但我没有足够的自信将其用于安全目的(例如,缺少一个神秘字符,您就没有防护罩),而snprintf可以应用于任何格式。。。哦,等等,snprintf不在ANSI C中。它是(仅?)C99。这可能是选择另一个的(微弱的)理由

    嗯。你也可以使用strncpy,不是吗

    e、 g


    对于问题中的简单示例,两个调用之间的安全性可能没有太大差异。但是,在一般情况下,
    snprintf()
    可能更安全。一旦有了具有多个转换规范的更复杂的格式字符串,就很难(或几乎不可能)确保在不同的转换中准确地说明缓冲区长度,尤其是因为以前的转换不一定会产生固定数量的输出字符

    因此,我坚持使用
    snprintf()

    snprintf()
    (虽然与安全性无关)的另一个小优点是它可以告诉您需要多大的缓冲区

    最后一个注意事项-您应该在
    snprintf()
    调用中指定实际缓冲区大小-它将为您处理空终止符的计算:

    snprintf(buff, sizeof(buff), "%s", name);
    
    在我读到这篇文章之前,我会说
    snprintf()
    要好得多:

    简短的总结是:
    snprintf()
    不可移植,它的行为会随着系统的不同而变化。
    snprintf()
    最严重的问题可能发生在通过调用
    sprintf()
    来实现
    snprintf()
    时。您可能认为它可以保护您免受缓冲区溢出,降低您的警惕性,但可能不会


    所以现在我仍然在说
    snprintf()
    更安全,但在使用时也要谨慎。

    最好、最灵活的方法是使用
    snprintf

    size_t nbytes = snprintf(NULL, 0, "%s", name) + 1; /* +1 for the '\0' */
    char *str = malloc(nbytes);
    snprintf(str, nbytes, "%s", name);
    

    在C99中,
    snprintf
    返回写入字符串的字节数,不包括
    '\0'
    。如果少于所需的字节数,
    snprintf
    返回扩展格式所需的字节数(仍不包括
    '\0'
    )。通过传递
    snprintf
    一个长度为0的字符串,您可以提前找到扩展字符串的长度,并使用它分配必要的内存。

    这两者之间有一个重要的区别--
    snprintf
    调用将
    name
    参数扫描到底(终止NUL)以便计算出正确的返回值。另一方面,
    sprintf
    调用将从
    name
    读取最多255个字符


    因此,如果
    name
    是指向至少255个字符的非NUL终止缓冲区的指针,那么
    snprintf
    调用可能会从缓冲区的末尾运行并触发未定义的行为(例如崩溃),而
    sprintf
    版本则不会。实际上,大约是1,我弄错了-它确实指定了最大字符数(但没有计算
    \0
    )。我已经相应地编辑了我的答案。好吧,这取决于OP的问题是关于什么样的安全性的。正式使用的
    sprintf
    在这种特定情况下与
    snprintf
    一样安全。在这个答案中,您所说的是缺乏对懒惰/不称职程序员的保护。我不知道OP是否询问安全性的这一方面。我假设字符串
    name
    是由不受信任的用户提供的;这是唯一一个安全问题,在这种情况下,这是一个相当严重的问题。使用一个非常糟糕的
    sprintf
    格式字符串,恶意用户精心设计其输入可以覆盖堆栈并执行任意代码;使用此格式字符串,恶意用户只能用零(空终止符)覆盖堆栈上的一个字节,这可能是一种非常有效的DOS攻击。但是,一个好的格式字符串提供与
    snprintf
    相同级别的保护(同样,专门应用于字符串输入)。它确实,但是它的可验证性要差得多-很难查看
    sprintf
    调用,很难一眼就确定它将覆盖的内存上限。您确定ANSI C中有
    %.*s
    吗?我
    size_t nbytes = snprintf(NULL, 0, "%s", name) + 1; /* +1 for the '\0' */
    char *str = malloc(nbytes);
    snprintf(str, nbytes, "%s", name);