FlawOpen

flawopen.com/Speichersicherheit/strcpy vs strncpy

Ist strncpy() sicherer als strcpy()?

CWE-787: Schreiben außerhalb des Bereichs; CWE-170: fehlerhafte NullterminierungReferenzseite
Kurzantwort

Ein Feld mit fester Breite ist wie eine Reihe markierter Plätze: Der letzte braucht ein Endzeichen, wenn der Code die Bytes als C-String behandelt. strncpy() schreibt höchstens n Bytes und verhindert einen Überlauf nur, wenn n nicht größer als der echte Puffer ist. Füllt die Quelle dieses Limit aus, hängt die Funktion keinen Nullterminator an. Eine spätere Stringfunktion kann dann über das Ziel hinaus nach dem fehlenden Zeichen lesen.

BEIDE SIND FALSCH
char dst[16];

/* 1. Unbounded — classic overflow */
strcpy(dst, src);

/* 2. Bounded, but may not terminate.
   If strlen(src) >= 16, dst has no
   '\0' and every later strlen/printf
   reads out of bounds.            */
strncpy(dst, src, sizeof(dst));

/* 3. Off-by-one: room for 16 chars
   plus a terminator = 17 bytes,
   but dst only holds 16.          */
strncpy(dst, src, sizeof(dst));
dst[sizeof(dst)] = '\0';   /* OOB! */
KORREKT
char dst[16];

/* Preferred: snprintf always
   null-terminates (when size > 0),
   and tells you about truncation. */
int n = snprintf(dst, sizeof(dst),
                 "%s", src);
if (n < 0 || (size_t)n >= sizeof(dst)) {
    /* truncated — handle it */
    return ERR_TOO_LONG;
}

/* Or strlcpy where available —
   always terminates, returns the
   length it tried to create.      */
if (strlcpy(dst, src, sizeof(dst))
        >= sizeof(dst)) {
    return ERR_TOO_LONG;
}

Wofür strncpy ursprünglich gedacht war

Das Verhalten ist kein Fehler: strncpy() wurde für Felder fester Breite mit Nullauffüllung entwickelt, nicht als allgemein sichere C-String-Kopie. Ist der Quellstring kürzer als n, füllt die Funktion das Ziel bis Byte n - 1 mit Nullbytes auf. Gibt es in den ersten n Quellbytes kein Nullbyte, kopiert sie genau n Bytes und terminiert das Ziel nicht. Das passt zu manchen Festfeldformaten, ist aber gefährlich für APIs, die einen nullterminierten String erwarten.

Bei einer Quelle kürzer als n schreibt die nötige Auffüllung bis zur gesamten Feldbreite. Bei großem Ziel und kurzer Quelle kann das mehr Arbeit verursachen als eine Kopie, die am Terminator stoppt.

Trunkierung immer prüfen

Einen Überlauf zu verhindern ist nur die halbe Aufgabe. Stille Trunkierung ist ebenfalls riskant: Ein gekürzter Hostname, Pfad oder Autorisierungswert kann seine Bedeutung ändern. snprintf gibt die Länge zurück, die es schreiben wollte; vergleiche sie mit der Puffergröße. Die Fehlinterpretation als tatsächlich geschriebene Bytes spielte auch bei Citrix Bleed eine Rolle.

Häufige Fragen

Was ist mit strcpy_s und den Annex-K-Funktionen?

Sie terminieren und prüfen Grenzen, sind aber unterschiedlich portabel: unter Windows gut unterstützt, andernorts uneinheitlich implementiert. Für mehrere Plattformen ist snprintf meist besser verfügbar.

Ist strlcpy Teil des C-Standards?

Nein. Die Funktion stammt aus OpenBSD und ist auf BSD und macOS verfügbar; neuere glibc-Versionen enthalten sie ebenfalls. Prüfe die Verfügbarkeit für deine Zielsysteme.

Sollte ich diese Funktionen in C++ verwenden?

Bevorzuge std::string und std::string_view, die Längen verwalten. C-Funktionen gehören an API-Grenzen, die rohe Puffer verlangen.

Quellen

Siehe auch CWE vs. CVE: Was ist der Unterschied? Mass Assignment: Risiken und sichere Feldfreigaben Serverless State Bleed: Benutzerdaten pro Anfrage isolieren PHP == und Magic-Hash-Vergleiche Ist Rusts unsafe-Block weiterhin speichersicher?