ventajas programacion paradigmas orientada objetos imperativa funcional ejemplos desventajas security functional-programming

security - paradigmas - programacion funcional vs orientada a objetos



¿Vulnerabilidad en el paradigma de programación funcional? (4)

Hace unos días, hubo un par de preguntas sobre las vulnerabilidades de desbordamiento del búfer (como ¿tiene Java desbordamientos de búfer?, Secure C y las universidades -entrenamiento para desbordamiento de búfer , por nombrar un par) lo que puede suceder en lenguajes de programación imperativos como C .

En la programación funcional, (de la exposición muy limitada que he tenido al probar Haskell ), puedo ver cómo las vulnerabilidades como el desbordamiento del búfer no se producirían porque esos problemas son el resultado de cambiar el estado de un programa o un área de memoria. (Por favor, corríjame si estoy equivocado.)

Sin tener en cuenta la posibilidad de vulnerabilidades presentes en el compilador, el intérprete o el entorno de ejecución, ¿existe algún tipo de vulnerabilidad de seguridad en el paradigma de programación funcional? ¿Existen tipos específicos de vulnerabilidades que existen en la programación funcional, pero no en la programación imperativa?


Los lenguajes funcionales tienen una ventaja infravalorada de "seguridad a través de la oscuridad" debido a sus modelos de ejecución. Si observa los exploits de seguridad en los programas C, aprovechan el sistema de tipo débil, la manipulación de punteros y la falta de comprobación de límites, pero lo más importante es que aprovechan un modelo de ejecución bien entendido y directo. Por ejemplo, puede romper la pila de manera confiable en C, porque es relativamente fácil saber dónde está la pila , simplemente tomando la dirección de las variables locales. Muchos otros exploits confían en una comprensión similar de bajo nivel del modelo de ejecución.

Por el contrario, no es tan obvio cómo el código funcional se compilará en un archivo binario, por lo que no es tan fácil idear una receta para ejecutar el código inyectado o acceder a datos privilegiados. Irónicamente, la oscuridad de los modelos de ejecución generalmente se considera una debilidad de los lenguajes funcionales, ya que los programadores no siempre tienen una buena intuición de cómo funcionará su código.


No lo creo.

Tal como lo veo, en lugar de programar un paradigma, las vulnerabilidades como los desbordamientos de los búfers tienen más que ver con la arquitectura / máquina virtual del compilador / intérprete.

Por ejemplo, si está utilizando una programación funcional en un entorno .NET (C #, VB, etc.), siempre que trabaje con objetos gestionados, se ocuparán de los desbordamientos del búfer.


Puede ser más fácil provocar recursiones sin límites (y el uso de la memoria resultante) en una implementación funcional que en la implementación imperativa correspondiente. Mientras que un programa imperativo podría entrar en un bucle infinito cuando procesa datos de entrada mal formados, un programa funcional puede engullir toda la memoria que pueda mientras realiza el mismo procesamiento. En general, sería la responsabilidad de la VM en la que se ejecuta el programa limitar la cantidad de memoria que puede consumir un proceso en particular. (Incluso para procesos Unix nativos, ulimit puede proporcionar esta protección).


Si el programador no anticipa que [alguna entrada] podría hacer que [programa] consumiera recursos más que disponibles, esa es una vulnerabilidad en la forma de un posible DoS. Esta es una debilidad de todos los lenguajes completos de Turing que he visto, pero la pereza de Haskell hace que sea más difícil razonar sobre lo que implica un cálculo.

Como un ejemplo (más bien artificial),

import Control.Monad (when) import System (getArgs) main = do files <- getArgs contents <- mapM readFile files flip mapM_ (zip files contents) $ /(file, content) -> when (null content) $ putStrLn $ file ++ " is empty"

El programador ingenuo puede pensar: "Haskell es flojo, por lo que no se abrirá y leerá los archivos hasta que sea necesario", y "Haskell es basura recolectada, así que una vez que se hace con un archivo, puede cerrar el manejador del archivo". Desafortunadamente, este programa solo abrirá muchos archivos a la vez (específicos de la implementación) y solo los archivos vacíos cerrarán sus manejadores de archivos (efecto secundario de las reglas de activación de la implementación):

$ ghc --make -O2 Test [1 of 1] Compiling Main ( Test.hs, Test.o ) Linking Test ... $ strace -etrace=open,close ./Test dir/* /dev/null ... open("dir/1", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 3 open("dir/2", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 4 open("dir/3", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 5 open("dir/4", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 6 open("dir/5", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 7 ... open("/dev/null", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 255 close(255) /dev/null is empty $

Es posible que no espere que se produzca el error -EMFILE "Demasiados archivos abiertos".

Como dije, este es un ejemplo inventado, y también puede ocurrir en otros idiomas, pero es más fácil pasar por alto ciertos usos de los recursos en Haskell.