Arpita
Deseo saber cuál de estas dos opciones es la más segura de usar:
#define MAXLEN 255
char buff[MAXLEN + 1]
-
sprintf(buff, "%.*s", MAXLEN, name)
-
snprintf(buff, MAXLEN, "%s", name)
Tengo entendido que ambos son iguales. Por favor recomiende.
azerí
Las dos expresiones que diste son no equivalente: 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 tiene espacio en el búfer y, al hacerlo, escribir código arbitrario. los %.*s
no es una solución satisfactoria porque:
- 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 de su longitud en la memoria (es decir, no cuenta el terminador nulo). - Cualquier cambio en la cadena de formato (agregar una nueva línea, por ejemplo) cambiará el comportamiento de la
sprintf
versión con respecto a los desbordamientos de búfer. Consnprintf
se establece un máximo fijo y claro independientemente de los cambios en la cadena de formato o los tipos de entrada.
-
En realidad, sobre 1, me equivoqué: especifica el número máximo de caracteres (pero sin contar los
\0
). He editado mi respuesta en consecuencia.– Eli Iser
6 de septiembre de 2011 a las 7:06
-
Bueno, todo esto depende de qué tipo de seguridad se trate la pregunta del OP. Formalmente un uso adecuado
sprintf
es tan seguro en este caso específico comosnprintf
. De lo que está hablando en esta respuesta es la falta de protección contra el programador perezoso/incompetente. Si el OP estaba preguntando sobre este lado de la seguridad, no lo sé.– AnT apoya a Rusia
6 de septiembre de 2011 a las 7:56
-
Supongo que la cadena
name
es proporcionado por un usuario que no es de confianza; ese es el único caso en el que se trata de un problema de seguridad, y en ese caso es bastante serio. con una muy malasprintf
cadena de formato, un usuario malintencionado que elabore cuidadosamente su entrada puede sobrescribir la pila y ejecutar código arbitrario; con esta cadena de formato, un usuario malintencionado solo puede sobrescribir un byte en la pila con un cero (el terminador nulo), lo que podría ser un ataque de DOS bastante efectivo.– azernik
6 de septiembre de 2011 a las 7:59
-
Aún un bueno cadena de formato proporciona el mismo nivel de protección que
snprintf
(nuevamente, aplicado específicamente a la entrada de cadenas).– AnT apoya a Rusia
6 de septiembre de 2011 a las 8:06
-
Lo hace, pero es mucho menos verificable: es difícil mirar un
sprintf
llamar y, de un vistazo, establecer un límite superior a la memoria que sobrescribirá.– azernik
6 de septiembre de 2011 a las 8:14
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()
probablemente sea más seguro. Una vez que tenga una cadena de formato más compleja con múltiples especificaciones de conversión, puede ser difícil (o casi imposible) asegurarse de que la longitud del búfer se tenga en cuenta con precisión en las diferentes conversiones, especialmente porque las conversiones anteriores no necesariamente producen un número fijo. de caracteres de salida.
Entonces, me quedaría con snprintf()
.
Otra pequeña ventaja para snprintf()
(aunque no está relacionado con la seguridad) es que le dirá qué tan grande es el búfer que necesita.
Una nota final: debe especificar el tamaño real del búfer en el snprintf()
llamada: se encargará de la contabilidad del terminador nulo por usted:
snprintf(buff, sizeof(buff), "%s", name);
-
Creo que vale la pena señalar explícitamente que para su última línea de código,
buff
tiene que ser una matriz en la pila. Si tienes unchar *
, no funcionará. (Así que puede quepero no funcionará todo el tiempo, al menos). Sí, funciona en el caso de OPpero la razón por la cual es sutil para las personas nuevas que usan esto como referencia.– Nic
27/06/2018 a las 21:55
-
Nic, ¿puedes respaldar la afirmación “tiene que ser una matriz en la pila” con una cita?
– Ryan V. Bissell
16 de noviembre de 2021 a las 22:31
Giorgian Borca-Tasciuc
La forma mejor y más flexible sería utilizar 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 hubiera menos de la cantidad necesaria de bytes, snprintf
devuelve el número de bytes que habrían sido necesarios para expandir el formato (todavía excluyendo el '\0'
). al pasar snprintf
una cadena de longitud 0, puede averiguar de antemano cuánto tiempo habría sido la cadena expandida y usarla para asignar la memoria necesaria.
-
Sí, este es el mejor enfoque, no sé por qué esta respuesta no se vota en la parte superior. Los usuarios generalmente no saben que pueden poner NULL como primer argumento.
– usuario26742873
8 de agosto de 2020 a las 15:06
-
man7.org/linux/man-pages/man3/asprintf.3.html lo lleva un paso más allá y simplemente lo aplica por usted. extensión GNU.
– Daniel Farrell
11 de septiembre de 2020 a las 1:02
Kadir Erdem Demir
yo diría snprintf()
es mucho más mejor hasta que leí este pasaje:
https://buildsecurityin.us-cert.gov/bsi/articles/knowledge/coding/838-BSI.html
El breve resumen es: snprintf()
no portable su cambio de comportamiento de un sistema a otro. El problema más serio con snprintf()
puede ocurrir cuando snprintf()
se implementa simplemente llamando sprintf()
Puede pensar que lo protege del desbordamiento del búfer y bajar la guardia, pero puede que no sea así.
Así que ahora sigo diciendo snprintf()
más seguro, pero también siendo cauteloso cuando lo uso.
Hay una diferencia importante entre estos dos: el snprintf
la llamada escaneará el name
argumento hasta el final (terminando NUL) para averiguar el valor de retorno correcto. los sprintf
la llamada, por otro lado, leerá COMO MÁXIMO 255 caracteres de name
.
Así que si name
es un puntero a un búfer no terminado en NUL con al menos 255 caracteres, el snprintf
la llamada podría salirse del final del búfer y desencadenar un comportamiento indefinido (como bloquearse), mientras que el sprintf
la versión no lo hará.
Su declaración de sprintf es correcta, pero no estaría lo suficientemente seguro de sí mismo como para usar eso por motivos de seguridad (por ejemplo, si le falta un carácter críptico y no tiene escudo), mientras que hay snprintf que se puede aplicar a cualquier formato … oh Espere snprintf no está en ANSI C. Es (¿solo?) C99. Esa podría ser una razón (débil) para preferir la otra.
Bueno. podrías usar strncpy
también, ¿tú no podrías?
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
Ambos le darán el resultado que desea, pero snprintf
es más genérico y protegerá su cadena de desbordamientos sin importar la cadena de formato dada.
Además, porque snprintf
(o sprintf
para el caso) agrega una última \0
debe hacer que el búfer de cadena sea un byte más grande, char buff[MAXLEN + 1]
.
-
Ese resulta no ser el caso. Del documento snprintf: “Las funciones snprintf() y vsnprintf() escriben como máximo bytes de tamaño (incluido el byte nulo de terminación (‘\ 0’)) en str”.
– Chriszuma
2 abr 2012 a las 19:58
Cambiar #2 a
MAXLEN+1
y serán idénticos en lo que escribenbuff
en todos los casos (el valor de retorno será diferente sistrlen(name) > 255
).– Chris Dodd
22 de agosto de 2017 a las 16:27