Подтвердить что ты не робот

Какой из sprintf/snprintf более безопасен?

Я хочу знать, какой из этих двух вариантов более безопасен для использования:

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

  • snprintf(buff, MAXLEN, "%s", name)

Я понимаю, что оба они одинаковы. Пожалуйста, предложите.

4b9b3361

Ответ 1

Два приведенных вами выражения не эквивалентны: sprintf не принимает аргумента, указывающего максимальное количество байтов для записи; он просто берет буфер назначения, строку формата и кучу аргументов. Поэтому он может писать больше байтов, чем для вашего буфера, и при этом писать произвольный код. %.*s не является удовлетворительным решением, потому что:

  • Когда спецификатор формата относится к длине, он ссылается на эквивалент strlen; это мера количества символов в строке, а не ее длина в памяти (т.е. она не учитывает нулевой ограничитель).
  • Любое изменение в строке формата (например, добавление новой строки) изменит поведение версии sprintf в отношении переполнения буфера. При snprintf фиксированный максимальный максимум устанавливается независимо от изменений в строке формата или типах ввода.

Ответ 2

Для простого примера в вопросе не может быть большой разницы в безопасности между двумя вызовами. Однако в общем случае snprintf(), вероятно, более безопасен. После того, как у вас будет более сложная строка формата с несколькими спецификациями преобразования, может быть сложно (или почти невозможно) обеспечить, чтобы длина буфера учитывалась точно в разных конверсиях, тем более что предыдущие конверсии не обязательно создают фиксированное число выходных символов.

Итак, я бы придерживался snprintf().

Еще одно небольшое преимущество для snprintf() (хотя и не связанное с безопасностью) заключается в том, что он расскажет вам, какой большой буфер вам нужен.

Последнее примечание - вы должны указать фактический размер буфера в вызове snprintf() - он обработает учет нулевого терминатора для вас:

snprintf(buff, sizeof(buff), "%s", name);

Ответ 3

Ваш оператор sprintf верен, но я не был бы достаточно уверен в себе, чтобы использовать его для целей безопасности (например, пропустить один загадочный char и вы беззащитный), в то время как есть snprintf вокруг, который может быть применен к любому format... oh wait snprintf не находится в ANSI C. Это (только?) C99. Это может быть (слабая) причина предпочитать другую.

Ну. Вы могли бы использовать strncpy тоже, не могли бы вы?

например.

  char buffer[MAX_LENGTH+1];
  buffer[MAX_LENGTH]=0;             // just be safe in case name is too long
  strncpy(buffer,MAX_LENGTH,name);  // strncpy will never overwrite last byte

Ответ 4

Я бы сказал, snprintf() намного лучше, пока я не прочитал этот отрывок:

https://buildsecurityin.us-cert.gov/bsi/articles/knowledge/coding/838-BSI.html

Краткое описание: snprintf() не переносится его поведение от системы к системе. Самая серьезная проблема с snprintf() может возникнуть, когда snprintf() реализуется просто путем вызова sprintf(). Вы можете подумать, что он защищает вас от переполнения буфера и позволяет вашему охраннику опуститься, но это может быть не так.

Итак, теперь я все еще говорю snprintf() безопаснее, но также осторожно, когда я его использую.

Ответ 5

Самый лучший и самый гибкий способ - использовать 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, вы можете заранее узнать, как долго была бы расширенная строка, и использовать ее для выделения необходимой памяти.

Ответ 6

Там важная разница между этими двумя - вызов snprintf будет отсканировать аргумент name до конца (завершение NUL), чтобы выяснить правильное возвращаемое значение. С другой стороны, вызов sprintf будет читать AT MOST 255 символов из name.

Итак, если name является указателем на буфер с не-NUL-концом с по меньшей мере 255 символами, вызов snprintf может запуститься с конца буфера и вызвать поведение undefined (например, сбой), тогда как версия sprintf не будет.

Ответ 7

Оба получат желаемый результат, но snprintf является более общим и защитит вашу строку от переполнения независимо от строки формата.

Кроме того, поскольку snprintf (или sprintf, если на то пошло) добавляет окончательный \0, вы должны сделать строковый буфер на один байт больше, char buff[MAXLEN + 1].