sprintf snprintf ejemplo c security unix printf secure-coding

sprintf - snprintf c ejemplo



¿Cuál de sprintf/snprintf es más seguro? (7)

Deseo saber cuál de estas dos opciones es la más segura de usar:

#define MAXLEN 255 char buff[MAXLEN + 1]

  1. sprintf(buff, "%.*s", MAXLEN, name)

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

Mi entendimiento es que ambos son lo mismo. Por favor recomiende.


¡La mejor y más flexible forma sería usar snprintf !

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

En C99, snprintf devuelve el número de bytes escritos en la cadena excluyendo el ''/0'' . Si había menos de la cantidad necesaria de bytes, snprintf devuelve el número de bytes que habría sido necesario para expandir el formato (aún excluyendo el ''/0'' ). Al pasar snprintf una cadena de longitud 0, puede averiguar con anticipación cuánto tiempo debería haber sido la cadena expandida y utilizarla para asignar la memoria necesaria.


Ambos darán el resultado que desee, pero snprintf es más genérico y protegerá su cadena de sobrepasos sin importar la cadena de formato dada.

Además, debido a que snprintf (o sprintf para el caso) agrega un /0 final, debe hacer que el búfer de cadena sea un byte más grande, char buff[MAXLEN + 1] .


Hay una diferencia importante entre estos dos: la llamada snprintf escaneará el argumento del name hasta el final (que termina en NUL) para determinar el valor de retorno correcto. La llamada sprintf por otro lado leerá AT MOST 255 caracteres desde su name .

Por lo tanto, si el name es un puntero a un búfer no terminado en NUL con al menos 255 caracteres, la llamada snprintf podría ejecutarse fuera del búfer y desencadenar un comportamiento indefinido (como colisión), mientras que la versión sprintf no lo hará.


Las dos expresiones que dio no son equivalentes: sprintf no toma ningún argumento que especifique el número máximo de bytes para escribir; simplemente toma un búfer de destino, una cadena de formato y un montón de argumentos. Por lo tanto, puede escribir más bytes de los que su búfer tiene espacio y, al hacerlo, escribir código arbitrario. El %.*s no es una solución satisfactoria porque:

  1. Cuando el especificador de formato se refiere a la longitud, se refiere al equivalente de strlen ; esta es una medida del número de caracteres en la cadena, no su longitud en la memoria (es decir, no cuenta el terminador nulo).
  2. Cualquier cambio en la cadena de formato (agregar una nueva línea, por ejemplo) cambiará el comportamiento de la versión de sprintf con respecto a los desbordamientos de búfer. Con snprintf , se establece un máximo fijo y claro independientemente de los cambios en la cadena de formato o los tipos de entrada.

Para el ejemplo simple en la pregunta, puede que no haya mucha diferencia en la seguridad entre las dos llamadas. Sin embargo, en el caso general snprintf() es probablemente más seguro. Una vez que tiene una cadena de formato más compleja con múltiples especificaciones de conversión, puede ser difícil (o casi imposible) asegurarse de tener la longitud del búfer contabilizada con precisión en las diferentes conversiones, especialmente dado que las conversiones anteriores no producen necesariamente un número fijo. de los caracteres de salida.

Entonces, me quedaría con snprintf() .

Otra pequeña ventaja de snprintf() (aunque no está relacionada con la seguridad) es que te dirá qué tamaño de memoria intermedia necesitas.

Una nota final: debe especificar el tamaño del búfer real en la llamada snprintf() ; se encargará de la contabilidad del terminador nulo por usted:

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


Su declaración de Sprintf es correcta, pero no estoy lo suficientemente seguro de sí mismo como para usarla con fines de seguridad (por ejemplo, me falta un carácter críptico y está sin pantalla) mientras hay un snprintf que se puede aplicar a cualquier formato ... oh espera snprintf no está en ANSI C. Es (¿solo?) C99. Esa podría ser una razón (débil) para preferir la otra.

Bien. Podrías usar strncpy también, ¿no?

p.ej

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


Yo diría que snprintf() es mucho mejor hasta que lea este pasaje:

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

Breve resumen es: snprintf() no portátil, su comportamiento cambia de sistema a sistema. El problema más grave con snprintf() puede ocurrir cuando snprintf() se implementa simplemente llamando a sprintf() . Puede pensar que lo protege del desbordamiento del búfer y baje la guardia, pero puede que no.

Así que ahora todavía estoy diciendo snprintf() más seguro, pero también siendo precavido cuando lo uso.