it-swarm.com.de

Was von sprintf/snprintf ist sicherer?

Ich möchte wissen, welche dieser beiden Optionen die sicherere ist:

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

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

Mein Verständnis ist, dass beide gleich sind. Bitte vorschlagen.

37
Arpit

Die zwei Ausdrücke, die Sie angegeben haben, sind nicht äquivalent: sprintf nimmt kein Argument an und gibt die maximale Anzahl der zu schreibenden Bytes an. Es braucht lediglich einen Zielpuffer, eine Formatzeichenfolge und eine Reihe von Argumenten. Daher schreibt es möglicherweise mehr Bytes, als in Ihrem Puffer Platz hat, und schreibt dabei beliebigen Code. Der %.*s ist keine zufriedenstellende Lösung, weil:

  1. Wenn sich der Formatbezeichner auf die Länge bezieht, bezieht er sich auf das Äquivalent von strlen; Dies ist ein Maß für die Anzahl der Zeichen in der Zeichenfolge, nicht für die Länge im Speicher (d. h., es zählt nicht das Nullabschlußzeichen).
  2. Jede Änderung in der Formatzeichenfolge (z. B. durch Hinzufügen eines Zeilenvorschubs) ändert das Verhalten der sprintf-Version in Bezug auf Pufferüberläufe. Mit snprintf wird unabhängig von Änderungen in der Formatzeichenfolge oder den Eingabetypen ein festes, eindeutiges Maximum festgelegt.
34
azernik

Für das einfache Beispiel in der Frage besteht möglicherweise kein großer Sicherheitsunterschied zwischen den beiden Anrufen. Im allgemeinen Fall ist snprintf() jedoch wahrscheinlich sicherer. Wenn Sie eine komplexere Formatzeichenfolge mit mehreren Konvertierungsspezifikationen haben, kann es schwierig (oder nahezu unmöglich) sein, sicherzustellen, dass die Pufferlänge für die verschiedenen Konvertierungen genau berücksichtigt wird. Dies gilt insbesondere, da eine vorherige Konvertierung nicht notwendigerweise eine feste Zahl ergibt von Ausgabezeichen.

Also würde ich bei snprintf() bleiben.

Ein weiterer kleiner Vorteil von snprintf() (wenn auch nicht sicherheitsbezogen) ist, dass Sie wissen, wie viel Puffer Sie benötigen.

Eine letzte Anmerkung - Sie sollten die tatsächliche Puffergröße im Aufruf von snprintf() angeben. Sie übernimmt die Abrechnung des Null-Terminators für Sie:

snprintf(buff, sizeof(buff), "%s", name);
10
Michael Burr

Ich würde sagen, snprintf() ist viel besser, bis ich diesen Abschnitt gelesen habe: 

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

Kurze Zusammenfassung ist: snprintf() nicht portabel. Das Verhalten ändert sich von System zu System. Das schwerwiegendste Problem mit snprintf() kann auftreten, wenn snprintf() einfach durch Aufrufen von sprintf() implementiert wird. Möglicherweise werden Sie davon ausgehen, dass es Sie vor Pufferüberlauf schützt und Ihre Wachsamkeit verliert.

Also sage ich jetzt noch snprintf() sicherer, aber auch vorsichtig, wenn ich es benutze.

5

Ihre Sprintf-Aussage ist korrekt, aber ich wäre nicht selbstbewusst genug, um dies aus Sicherheitsgründen zu verwenden (z. B. wenn ein kryptisches Zeichen fehlt und Sie schirmlos sind), während Snprintf in der Lage ist, das auf jedes Format angewendet werden kann wait snprintf ist nicht in ANSI C . Es ist (nur?) C99. Das könnte ein (schwacher) Grund sein, den anderen zu bevorzugen.

Gut. Sie könnten auch strncpy verwenden, nicht wahr?

z.B. 

  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
2
PypeBros

Der beste und flexibelste Weg wäre, snprintf zu verwenden!

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

In C99 gibt snprintf die Anzahl der in den String geschriebenen Bytes außer '\0' zurück. Wenn weniger als die erforderliche Anzahl von Bytes vorhanden wäre, gibt snprintf die Anzahl der Bytes zurück, die zum Erweitern des Formats erforderlich wären (immer noch ohne '\0'). Indem Sie snprintf eine Zeichenfolge mit der Länge 0 übergeben, können Sie vorab feststellen, wie lang die erweiterte Zeichenfolge gewesen wäre, und damit den erforderlichen Speicher zuweisen.

Es gibt einen wichtigen Unterschied zwischen diesen beiden: Der Aufruf snprintf durchsucht das Argument name bis zum Ende (beendet NUL), um den korrekten Rückgabewert zu ermitteln. Der sprintf-Aufruf dagegen liest AT MOST 255 Zeichen aus name.

Wenn also name ein Zeiger auf einen nicht-NUL-terminierten Puffer mit mindestens 255 Zeichen ist, wird der snprintf-Aufruf möglicherweise am Ende des Puffers ablaufen und undefiniertes Verhalten (z. B. Absturz) auslösen, während die sprintf-Version dies nicht tut.

1
Chris Dodd

Beide geben das gewünschte Ergebnis, aber snprintf ist generischer und schützt Ihre Zeichenfolge vor Überschreitungen, unabhängig von der angegebenen Formatzeichenfolge.

Da snprintf (oder sprintf für diese Angelegenheit) einen abschließenden \0 hinzufügt, sollten Sie den Zeichenfolgenpuffer um ein Byte vergrößern, char buff[MAXLEN + 1].

0
Eli Iser