¿Por qué strlcpy y strlcat se consideran inseguros?

80

Entiendo eso strlcpyy strlcatfueron diseñados como reemplazos seguros para strncpyy strncat. Sin embargo, algunas personas todavía opinan que son inseguras y simplemente causan un tipo diferente de problema .

¿Alguien puede dar un ejemplo de cómo el uso de strlcpyo strlcat(es decir, una función que siempre termina en nulo sus cadenas) puede provocar problemas de seguridad?

Ulrich Drepper y James Antill afirman que esto es cierto, pero nunca brinden ejemplos ni aclaren este punto.

Anónimo
fuente

Respuestas:

112

En primer lugar, strlcpynunca se ha pensado como una versión segura de strncpy(y strncpynunca se ha pensado como una versión segura de strcpy). Estas dos funciones no están relacionadas en absoluto. strncpyes una función que no tiene ninguna relación con las cadenas C (es decir, cadenas terminadas en nulo). El hecho de que tenga el str...prefijo en su nombre es solo un error histórico. La historia y el propósito de strncpyson bien conocidos y están bien documentados. Esta es una función creada para trabajar con las llamadas cadenas de "ancho fijo" (no con cadenas C) utilizadas en algunas versiones históricas del sistema de archivos Unix. Algunos programadores de hoy se confunden con su nombre y asumen questrncpy alguna manera se supone que sirve como una función de copia de cadena C de longitud limitada (un hermano "seguro" destrcpy), que en realidad es una completa tontería y conduce a una mala práctica de programación. La biblioteca estándar de C en su forma actual no tiene ninguna función para la copia de cadena C de longitud limitada. Aquí es donde strlcpyencaja. De strlcpyhecho, es una verdadera función de copia de longitud limitada creada para trabajar con C-strings. strlcpyhace correctamente todo lo que debería hacer una función de copia de longitud limitada. La única crítica que se le puede hacer es que, lamentablemente, no es estándar.

En segundo lugar, strncatpor otro lado, es de hecho una función que trabaja con C-strings y realiza una concatenación de longitud limitada (de hecho es un hermano "seguro" de strcat). Para utilizar esta función correctamente el programador debe tener un cuidado especial, ya que el parámetro de tamaño que acepta esta función no es realmente el tamaño del búfer que recibe el resultado, sino el tamaño de su parte restante (también, el carácter terminador se cuenta implícitamente). Esto puede resultar confuso, ya que para vincular ese tamaño con el tamaño del búfer, el programador debe recordar realizar algunos cálculos adicionales, que a menudo se utilizan para criticar elstrncat .strlcatse encarga de estos problemas, cambiando la interfaz para que no sean necesarios cálculos adicionales (al menos en el código de llamada). Nuevamente, la única base que veo que uno puede criticar es que la función no es estándar. Además, las funciones de strcatgrupo es algo que no verá en el código profesional muy a menudo debido a la facilidad de uso limitada de la idea misma de la concatenación de cadenas basada en reexploración.

En cuanto a cómo estas funciones pueden provocar problemas de seguridad ... Simplemente no pueden. No pueden provocar problemas de seguridad en mayor medida de lo que el propio lenguaje C puede "provocar problemas de seguridad". Verá, durante bastante tiempo hubo un fuerte sentimiento de que el lenguaje C ++ tiene que moverse en la dirección de convertirse en un sabor extraño de Java. Este sentimiento a veces también se derrama en el dominio del lenguaje C, lo que resulta en una crítica bastante desorientada y forzada de las características del lenguaje C y las características de la biblioteca estándar C. Sospecho que podríamos estar lidiando con algo así en este caso también, aunque seguramente espero que las cosas no estén tan mal.

Hormiga
fuente
9
No estoy completamente de acuerdo. Sería bueno si strlcpyy strlcatreportaría algún tipo de condición de error si chocaran con el límite de tamaño del búfer de destino. Aunque puede verificar la longitud devuelta para probar esto, no es obvio. Pero creo que es una crítica menor. El argumento de 'fomentan el uso de cadenas C, por lo que son malas' es una tontería.
Omnifarious
7
"cómo estas funciones pueden conducir a problemas de seguridad" - fwiw Creo que el problema aquí es que algunas funciones C son más difíciles de usar correctamente que otras. Algunas personas creen erróneamente que existe un umbral especial de dificultad, por debajo del cual una función es "segura" y por encima del cual es "insegura". Tales personas son también por lo general de la creencia de que strcpyestá por encima del umbral y por lo tanto "inseguridad", y su función de cadena de copia preferido (si es strlcpy, strcpy_so incluso strncpy) está por debajo del umbral y por lo tanto "seguro".
Steve Jessop
31
Hay muchas razones por las que no le gusta strlcpy / strlcat, pero no indica ninguna de ellas. La discusión de C ++ y Java es irrelevante. Esta respuesta simplemente no es útil para el tema sobre el que se hizo la pregunta.
John Ripley
24
@John Ripley: En primer lugar, no estoy "declarando ninguno de ellos" simplemente porque no conozco ninguna razón por la que no me guste strlcpy/strlcat. A uno le podría "disgustar" el concepto general de cadena terminada en cero, pero de eso no se trata la pregunta. Si conoce "muchas razones para que no strlcpy/strlcatme guste ", probablemente debería escribir su propia respuesta en lugar de esperar que pueda leer la mente de otra persona.
2013
8
@John Ripley: En segundo lugar, la pregunta se refería específicamente a algunos supuestos "problemas de seguridad" con strlcpy/strlcat. Aunque creo que entiendo de qué se trata, personalmente me niego a reconocerlo como "problemas de seguridad" dentro del ámbito del lenguaje C tradicional, tal como lo conozco. Eso lo dije en mi respuesta.
2013
31

La crítica de Ulrich se basa en la idea de que un truncamiento de cadena que no es detectado por el programa puede generar problemas de seguridad, debido a una lógica incorrecta. Por lo tanto, para estar seguro, debe verificar el truncamiento. Hacer esto para una concatenación de cadenas significa que está haciendo una verificación a lo largo de las líneas de esto:

Ahora, strlcathaga esta verificación de manera efectiva, si el programador recuerda verificar el resultado, para que pueda usarlo de manera segura:

El punto de Ulrich es que dado que tienes que tener destleny sourcelenalrededor (o recalcularlos, que es lo que strlcatefectivamente hace), también podrías usar el más eficiente de memcpytodos modos:

(En el código anterior, dest_maxlenes la longitud máxima de la cadena en la que se puede almacenar dest, uno menos que el tamaño del destbúfer. dest_bufferlenEs el tamaño completo del dest buffer).

coste y flete
fuente
13
La legibilidad del código de Drepper es mala. Con strlcpy (o cualquier función str) sé directamente que estoy copiando una cadena C terminada en 0. Con memcpyél puede ser cualquier tipo de memoria y tengo una dimensión suplementaria para comprobar cuando intento entender el código. Tenía una aplicación heredada para depurar donde todo se hacía con memcpy, era un PITA real para corregir. Después de la migración a la función String dedicada, es mucho más fácil de leer (y más rápido porque strlense pueden eliminar muchas cosas innecesarias ).
Patrick Schlüter
3
@domen: Debido a que el tamaño a copiar ya se conoce, memcpy()es suficiente (y potencialmente más eficiente que strcpy()).
caf
1
Bueno, es confuso tenerlo en operaciones de cadena. Y hasta donde yo sé, la eficiencia depende de la implementación y no está estandarizada.
Domingo
8
@domen: memcpy() es una operación de cadena <string.h>; después de todo, está declarada .
caf
2
@domen Estoy de acuerdo en que existe la posibilidad de confusión, pero la realidad es que, de todos modos, trabajar con cadenas de C es prácticamente trabajar con memoria en bruto. Podría decirse que todos estaríamos mejor si la gente simplemente dejara de pensar que C tiene "cadenas" (a diferencia de cualquier otro fragmento de memoria contiguo).
mtraceur
19

Cuando la gente dice " strcpy()es peligroso, utilícelo strncpy()en su lugar" (o declaraciones similares sobre strcat()etc., pero lo voy a usar strcpy()aquí como mi enfoque), quieren decir que no hay límites strcpy(). Por lo tanto, una cadena demasiado larga resultará en saturaciones de búfer. Son correctos. El uso strncpy()en este caso evitará desbordamientos del búfer.

Siento que strncpy()realmente no soluciona errores: resuelve un problema que un buen programador puede evitar fácilmente.

Como programador de C, debe conocer el tamaño de destino antes de intentar copiar cadenas. Esa es la premisa de strncpy()y strlcpy()'s últimos parámetros demasiado: Usted suministro de ese tamaño a ellos. También puede conocer el tamaño de la fuente antes de copiar cadenas. Entonces, si el destino no es lo suficientemente grande, no llamestrcpy() . Reasigne el búfer o haga otra cosa.

¿Por qué no me gusta strncpy()?

  • strncpy() es una mala solución en la mayoría de los casos: su cadena se truncará sin previo aviso; prefiero escribir código adicional para resolver esto yo mismo y luego tomar el curso de acción que quiero tomar, en lugar de dejar que alguna función decida yo sobre qué hacer.
  • strncpy()es muy ineficiente. Escribe en cada byte del búfer de destino. No necesita esos miles '\0'al final de su destino.
  • No escribe una terminación '\0' si el destino no es lo suficientemente grande. Por lo tanto, debe hacerlo usted mismo de todos modos. La complejidad de hacer esto no vale la pena.

Ahora llegamos a strlcpy(). Los cambios de lo strncpy()hacen mejor, pero no estoy seguro de si el comportamiento específico destrl* justifica su existencia: son demasiado específicos. Aún tienes que conocer el tamaño del destino. Es más eficiente que strncpy()porque no escribe necesariamente en cada byte del destino. Pero los que resuelve un problema que puede ser resuelto haciendo: *((char *)mempcpy(dst, src, n)) = 0;.

No creo que nadie diga eso strlcpy() o strlcat()pueda dar lugar a problemas de seguridad, lo que ellos (y yo) estamos diciendo que puede resultar en errores, por ejemplo, cuando espera que se escriba la cadena completa en lugar de una parte de ella.

El problema principal aquí es: ¿cuántos bytes copiar? El programador debe saber esto y si no lo sabe,strncpy() o strlcpy()no lo salvará.

strlcpy()y strlcat()no son estándar, ni ISO C ni POSIX. Entonces, su uso en programas portátiles es imposible. De hecho, strlcat()tiene dos variantes diferentes: la implementación de Solaris es diferente de las otras para los casos de borde que involucran la longitud 0. Esto la hace aún menos útil que de otra manera.

Alok Singhal
fuente
3
strlcpyes más rápido que memcpyen muchas arquitecturas, especialmente si memcpycopia datos finales innecesarios. strlcpytambién devuelve la cantidad de datos que perdió, lo que podría permitirle una recuperación más rápida y con menos código.
1
@jbcreix: mi punto es que no debería haber datos que perder, y nen mi llamada de memcpy está el número exacto de bytes que se escribirán, por lo que la eficiencia tampoco es un gran problema.
Alok Singhal
5
¿Y cómo se obtiene esa n? El único n que puede conocer de antemano es el tamaño del búfer. Por supuesto, si sugiere volver a implementar strlcpycada vez que lo necesite memcpyy strleneso también está bien, pero entonces, ¿por qué detenerse en strlcpy? Tampoco necesita una memcpyfunción, puede copiar los bytes uno por uno. La implementación de referencia solo recorre los datos una vez en el caso normal y eso es mejor para la mayoría de las arquitecturas. Pero incluso si se utiliza la mejor implementación strlen+ memcpy, todavía no hay razón para no tener que volver a implementar una strcpy segura una y otra vez.
1
Alok, con strlcpyusted barre la cadena de entrada solo una vez en el caso bueno (que debería ser la mayoría) y dos veces en el caso malo, con su strlen+ memcpylo revisa dos veces siempre. Si marca la diferencia en la práctica, es otra historia.
Patrick Schlüter
4
"* ((char *) mempcpy (dst, src, n)) = 0;" - Correcto - Sí, obviamente correcto, No. No se revisa el código ......
Mattnz
12

Creo que Ulrich y otros creen que les dará una falsa sensación de seguridad. Truncar cadenas accidentalmente puede tener implicaciones de seguridad para otras partes del código (por ejemplo, si se trunca una ruta del sistema de archivos, es posible que el programa no esté realizando operaciones en el archivo deseado).

jamesdlin
fuente
8
Por ejemplo, un cliente de correo electrónico puede truncar el nombre de archivo de un archivo adjunto de correo electrónico de malware.exe.jpga malware.exe.
Chris Peterson
1
@ChrisPeterson Es por eso que un buen desarrollador siempre verifica los valores devueltos, para, en el caso de las funciones strl *, saber si los datos se truncaron y actuar en consecuencia.
Tom Lint
"Ulrich y otros piensan que les dará una falsa sensación de seguridad ...." - Lol ... mientras tanto, Ulrich y sus amigos hacen apariciones regulares en BugTraq y Full Disclosure por su única ocasión. Deben utilizar las funciones más seguras y evitar la mayoría de sus problemas. Entonces pueden comenzar a decirle a otros cómo escribir un código más seguro ...
jww
7

Hay dos "problemas" relacionados con el uso de funciones strl:

  1. Debe verificar los valores de retorno para evitar el truncamiento.

Los redactores de borradores estándar c1x y Drepper, argumentan que los programadores no verificarán el valor de retorno. Drepper dice que de alguna manera deberíamos conocer la longitud y usar memcpy y evitar las funciones de cadena por completo. El comité de estándares argumenta que la strcpy segura debería devolver un valor distinto de cero en el truncamiento a menos que la _TRUNCATEbandera indique lo contrario . La idea es que es más probable que las personas usen if (strncpy_s (...)).

  1. No se puede usar sin cadenas.

Algunas personas piensan que las funciones de cadena nunca deberían fallar incluso cuando se alimentan con datos falsos. Esto afecta a funciones estándar como strlen, que en condiciones normales se producirán por defecto. El nuevo estándar incluirá muchas de estas funciones. Los controles, por supuesto, tienen una penalización de rendimiento.

La ventaja de las funciones estándar propuestas es que puede saber cuántos datos perdió con las funciones strl .


fuente
tenga en cuenta que strncpy_sno es una versión segura strncpysino básicamente un strlcpyreemplazo.
6

No pienso strlcpyy me strlcatconsideran inseguro o menos no es la razón por la que no están incluidos en glibc - después de todo, glibc incluye strncpy e incluso strcpy.

Las críticas que recibieron fue que supuestamente son ineficientes, no inseguros. .

Según el documento Secure Portability de Damien Miller:

La API strlcpy y strlcat verifica correctamente los límites del búfer de destino, termina en nul en todos los casos y devuelve la longitud de la cadena de origen, lo que permite la detección de truncamiento. Esta API ha sido adoptada por la mayoría de los sistemas operativos modernos y muchos paquetes de software independientes, incluidos OpenBSD (donde se originó), Sun Solaris, FreeBSD, NetBSD, el kernel de Linux, rsync y el proyecto GNOME. La excepción notable es la biblioteca C estándar de GNU, glibc [12], cuyo responsable se niega rotundamente a incluir estas API mejoradas, etiquetándolas "basura BSD horriblemente ineficiente"[4], a pesar de la evidencia previa de que son más rápidos en la mayoría de los casos que las API que reemplazan [13]. Como resultado, más de 100 de los paquetes de software presentes en el árbol de puertos de OpenBSD mantienen sus propios reemplazos de strlcpy y / o strlcat o API equivalentes, lo que no es una situación ideal.

Por eso no están disponibles en glibc, pero no es cierto que no estén disponibles en Linux. Están disponibles en Linux en libbsd:

Están empaquetados en Debian y Ubuntu y otras distribuciones. También puede simplemente tomar una copia y usarla en su proyecto; es breve y está bajo una licencia permisiva:

rsp
fuente
3

La seguridad no es un booleano. Las funciones de C no son totalmente "seguras" o "inseguras", "seguras" o "inseguras". Cuando se usa incorrectamente, una simple operación de asignación en C puede ser "insegura". strlcpy () y strlcat () se pueden utilizar de forma segura (de forma segura) del mismo modo que strcpy () y strcat () se pueden utilizar de forma segura cuando el programador proporciona las garantías necesarias de un uso correcto.

El punto principal de todas estas funciones de cadena C, estándar y no tan estándar, es el nivel al que hacen un uso seguro / seguro fácil . strcpy () y strcat () no son triviales para usar de forma segura; esto se demuestra por la cantidad de veces que los programadores de C se han equivocado a lo largo de los años y se han producido vulnerabilidades y exploits desagradables. strlcpy () y strlcat () y para el caso, strncpy () y strncat (), strncpy_s () y strncat_s (), son un poco más fáciles de usar de forma segura, pero aún así, no triviales. ¿Son inseguros / inseguros? No más de lo que es memcpy (), cuando se usa incorrectamente.

rswindell
fuente