Какой из sprintf / snprintf более безопасен? - PullRequest
39 голосов
/ 06 сентября 2011

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

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

  2. snprintf(buff, MAXLEN, "%s", name)

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

Ответы [ 7 ]

35 голосов
/ 06 сентября 2011

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

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

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

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

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

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

snprintf(buff, sizeof(buff), "%s", name);
5 голосов
/ 24 января 2013

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

https://buildsecurityin.us -cert.gov / BSI / статьи / знания / кодирование / 838-BSI.html

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

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

2 голосов
/ 22 августа 2017

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

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

2 голосов
/ 15 февраля 2016

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

2 голосов
/ 06 сентября 2011

Ваше утверждение sprintf правильное, но я не был бы достаточно уверен в себе, чтобы использовать его в целях безопасности (например, пропуская один загадочный символ и вы без экрана), в то время как есть snprintf, который можно применить к любому формату... о, подождите 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
0 голосов
/ 06 сентября 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...