FlawOpen

flawopen.com/Seguridad de memoria/strcpy vs strncpy

¿strncpy() es más segura que strcpy()?

CWE-787: escritura fuera de límites; CWE-170: terminación nula incorrectaPágina de referencia
Respuesta breve

Un campo de ancho fijo es como una fila de casillas: la última necesita un marcador de fin si el código tratará los bytes como una cadena C. strncpy() escribe como máximo n bytes y solo evita rebasar el destino si n no supera el tamaño real del búfer. Si la fuente llena ese límite, no añade terminador nulo. Una función posterior puede leer más allá del destino buscando el marcador ausente.

AMBAS SON INCORRECTAS
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! */
CORRECTO
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;
}

Para qué se diseñó strncpy

Este comportamiento no es un defecto: strncpy() se diseñó para secuencias de caracteres de ancho fijo y rellenadas con nulos, no como copia segura general de cadenas C. Si la cadena fuente es más corta que n, rellena el destino con bytes nulos hasta n - 1. Si no hay un byte nulo entre los primeros n bytes de la fuente, copia exactamente n bytes y no termina el destino. Puede servir para campos de ancho fijo, pero es peligrosa si el resultado se pasa a API que esperan una cadena terminada en nulo.

Cuando la fuente es más corta que n, el relleno requerido escribe hasta el ancho completo del campo. En búferes grandes y fuentes cortas, puede hacer más trabajo que una copia que se detiene en el terminador.

Comprueba siempre el truncamiento

Evitar el desbordamiento es solo la mitad. El truncamiento silencioso también puede ser un problema: un host, una ruta o un valor de autorización truncado puede cambiar de significado. snprintf devuelve la longitud que habría escrito; compárala con el tamaño del búfer. Interpretar mal ese valor como bytes escritos también fue relevante en Citrix Bleed.

Preguntas frecuentes

¿Y strcpy_s y las funciones Annex K?

Terminan la cadena y comprueban límites, pero su portabilidad es irregular: funcionan bien en Windows y están implementadas de forma desigual en otros sistemas. Para varios sistemas, snprintf suele estar más disponible.

¿strlcpy forma parte del estándar C?

No. Surgió en OpenBSD y está disponible en BSD y macOS; versiones recientes de glibc también la incluyen. Comprueba tus plataformas de destino.

En C++, ¿debo usar alguna de estas funciones?

Prefiere std::string y std::string_view, que gestionan la longitud. Usa funciones C solo en límites de API que exijan un búfer sin procesar.

Referencias

Véase también CWE frente a CVE: ¿cuál es la diferencia? Mass assignment: riesgos y listas seguras de campos Filtración de estado serverless: aísla datos por solicitud PHP == y las comparaciones de magic hash ¿El bloque unsafe de Rust sigue siendo seguro?