En ciencias de la computación , la seguridad basada en lenguaje ( LBS ) es un conjunto de técnicas que pueden usarse para fortalecer la seguridad de las aplicaciones en un alto nivel mediante el uso de las propiedades de los lenguajes de programación. Se considera que LBS refuerza la seguridad informática a nivel de aplicación, lo que hace posible prevenir vulnerabilidades que la seguridad del sistema operativo tradicional no puede manejar.
Las aplicaciones de software generalmente se especifican e implementan en ciertos lenguajes de programación , y para protegerse contra ataques, fallas y errores a los que el código fuente de una aplicación puede ser vulnerable, existe la necesidad de seguridad a nivel de la aplicación; seguridad evaluando el comportamiento de las aplicaciones con respecto al lenguaje de programación. Esta área se conoce generalmente como seguridad basada en el idioma.
Motivación
El uso de grandes sistemas de software, como SCADA , se está produciendo en todo el mundo [1] y los sistemas informáticos constituyen el núcleo de muchas infraestructuras. La sociedad depende en gran medida de infraestructuras como el agua, la energía, las comunicaciones y el transporte, que de nuevo dependen de sistemas informáticos que funcionen plenamente. Hay varios ejemplos bien conocidos de casos en los que los sistemas críticos fallan debido a errores o errores en el software, como cuando la escasez de memoria de la computadora provocó el colapso de las computadoras LAX y el retraso de cientos de vuelos (30 de abril de 2014). [2] [3]
Tradicionalmente, los mecanismos utilizados para controlar el comportamiento correcto del software se implementan a nivel del sistema operativo. El sistema operativo maneja varias posibles violaciones de seguridad, como violaciones de acceso a la memoria, violaciones de desbordamiento de pila, violaciones de control de acceso y muchas otras. Esta es una parte crucial de la seguridad en los sistemas informáticos; sin embargo, al asegurar el comportamiento del software en un nivel más específico, se puede lograr una seguridad aún mayor. Dado que muchas propiedades y comportamientos del software se pierden en la compilación, es significativamente más difícil detectar vulnerabilidades en el código de máquina. Al evaluar el código fuente, antes de la compilación, también se puede considerar la teoría y la implementación del lenguaje de programación, y se pueden descubrir más vulnerabilidades.
"Entonces, ¿por qué los desarrolladores siguen cometiendo los mismos errores? En lugar de depender de la memoria de los programadores, deberíamos esforzarnos por producir herramientas que codifiquen lo que se conoce sobre las vulnerabilidades de seguridad comunes e integrarlo directamente en el proceso de desarrollo".
- D. Evans y D. Larochelle, 2002
Objetivo de la seguridad basada en el lenguaje
Al usar LBS, la seguridad del software se puede aumentar en varias áreas, dependiendo de las técnicas utilizadas. Los errores de programación comunes, como permitir que se produzcan desbordamientos de búfer y flujos de información ilegales, pueden detectarse y rechazarse en el software utilizado por el consumidor. También es deseable proporcionar alguna prueba al consumidor sobre las propiedades de seguridad del software, haciendo que el consumidor pueda confiar en el software sin tener que recibir el código fuente y autocomprobarlo en busca de errores.
Un compilador, tomando el código fuente como entrada, realiza varias operaciones específicas del lenguaje en el código para traducirlo a código legible por máquina. El análisis léxico , el preprocesamiento , el análisis sintáctico , el análisis semántico , la generación de código y la optimización de código son operaciones de uso común en los compiladores. Al analizar el código fuente y utilizar la teoría y la implementación del lenguaje, el compilador intentará traducir correctamente el código de alto nivel en código de bajo nivel, preservando el comportamiento del programa.
Durante la compilación de programas escritos en un lenguaje de seguridad de tipos , como Java , el código fuente debe comprobarse correctamente antes de la compilación. Si la verificación de tipo falla, no se realizará la compilación y será necesario modificar el código fuente. Esto significa que, dado un compilador correcto, cualquier código compilado a partir de un programa fuente verificado correctamente debe estar libre de errores de asignación no válidos. Esta es información que puede ser valiosa para el consumidor de código, ya que proporciona cierto grado de garantía de que el programa no se bloqueará debido a algún error específico.
Un objetivo de LBS es garantizar la presencia de ciertas propiedades en el código fuente correspondiente a la política de seguridad del software. La información recopilada durante la compilación se puede utilizar para crear un certificado que se puede proporcionar al consumidor como prueba de seguridad en el programa dado. Tal prueba debe implicar que el consumidor puede confiar en el compilador utilizado por el proveedor y que el certificado, la información sobre el código fuente, puede ser verificada.
La figura ilustra cómo se puede establecer la certificación y verificación de código de bajo nivel mediante el uso de un compilador certificador. El proveedor de software obtiene la ventaja de no tener que revelar el código fuente y el consumidor se queda con la tarea de verificar el certificado, que es una tarea fácil en comparación con la evaluación y compilación del código fuente en sí. La verificación del certificado solo requiere una base de código de confianza limitada que contenga el compilador y el verificador.
Técnicas
Análisis de programas
Las principales aplicaciones del análisis de programas son la optimización del programa (tiempo de ejecución, requisitos de espacio, consumo de energía, etc.) y la corrección del programa (errores, vulnerabilidades de seguridad, etc.). El análisis de programa se puede aplicar a la compilación ( análisis estático ), al tiempo de ejecución ( análisis dinámico ) o ambos. En la seguridad basada en el lenguaje, el análisis de programas puede proporcionar varias características útiles, tales como: verificación de tipo (estática y dinámica), monitoreo , verificación de contaminación y análisis de flujo de control .
Análisis de flujo de información
El análisis del flujo de información se puede describir como un conjunto de herramientas que se utilizan para analizar el control del flujo de información en un programa, con el fin de preservar la confidencialidad e integridad cuando los mecanismos regulares de control de acceso fallan.
"Al disociar el derecho a acceder a la información del derecho a difundirla, el modelo de flujo va más allá del modelo de matriz de acceso en su capacidad para especificar un flujo de información seguro. Un sistema práctico necesita control de flujo y acceso para satisfacer todos los requisitos de seguridad".
- D. Denning, 1976
El control de acceso impone controles sobre el acceso a la información, pero no le preocupa lo que suceda después de eso. Un ejemplo: un sistema tiene dos usuarios, Alice y Bob. Alice tiene un archivo secret.txt , que solo ella puede leer y editar, y prefiere guardar esta información para sí misma. En el sistema, también existe un archivo public.txt , que todos los usuarios del sistema pueden leer y editar de forma gratuita. Ahora suponga que Alice ha descargado accidentalmente un programa malicioso. Este programa puede acceder al sistema como Alice, sin pasar por la verificación de control de acceso en secret.txt . El programa malicioso luego copia el contenido de secret.txt y lo coloca en public.txt , permitiendo que Bob y todos los demás usuarios lo lean. Esto constituye una violación de la política de confidencialidad prevista del sistema.
No interferencia
La no interferencia es una propiedad de los programas que no filtra ni revela información de variables con una clasificación de seguridad más alta , dependiendo de la entrada de variables con una clasificación de seguridad más baja . Un programa que satisface la no interferencia debe producir la misma salida siempre que se utilice la misma entrada correspondiente en las variables inferiores . Esto debe ser válido para todos los valores posibles de la entrada. Esto implica que incluso si las variables superiores en el programa tienen valores diferentes de una ejecución a otra, esto no debería ser visible en las variables inferiores .
Un atacante podría intentar ejecutar un programa que no satisfaga la no interferencia repetida y sistemáticamente para intentar mapear su comportamiento. Varias iteraciones podrían llevar a la divulgación de variables superiores y permitir que el atacante obtenga información confidencial sobre, por ejemplo, el estado del sistema.
Si un programa satisface la no interferencia o no, se puede evaluar durante la compilación asumiendo la presencia de sistemas de tipo de seguridad .
Sistema de tipo de seguridad
Un sistema de tipos de seguridad es un tipo de sistema que pueden utilizar los desarrolladores de software para comprobar las propiedades de seguridad de su código. En un lenguaje con tipos de seguridad, los tipos de variables y expresiones se relacionan con la política de seguridad de la aplicación, y los programadores pueden especificar la política de seguridad de la aplicación mediante declaraciones de tipo. Los tipos pueden usarse para razonar sobre varios tipos de políticas de seguridad, incluidas las políticas de autorización (como control de acceso o capacidades) y la seguridad del flujo de información. Los sistemas de tipo de seguridad pueden relacionarse formalmente con la política de seguridad subyacente, y un sistema de tipo de seguridad es sólido si todos los programas que verifican el tipo satisfacen la política en un sentido semántico. Por ejemplo, un sistema de tipos de seguridad para el flujo de información podría imponer la no interferencia, lo que significa que la verificación de tipos revela si existe alguna violación de la confidencialidad o integridad en el programa.
Asegurar el código de bajo nivel
Las vulnerabilidades en el código de bajo nivel son errores o fallas que llevarán al programa a un estado en el que el lenguaje de programación fuente no define el comportamiento adicional del programa. El comportamiento del programa de bajo nivel dependerá de los detalles del compilador, del sistema de ejecución o del sistema operativo. Esto permite que un atacante lleve el programa a un estado indefinido y explote el comportamiento del sistema.
Las vulnerabilidades comunes de código inseguro de bajo nivel permiten a un atacante realizar lecturas o escrituras no autorizadas en direcciones de memoria. Las direcciones de memoria pueden ser aleatorias o elegidas por el atacante.
Usar lenguajes seguros
Un enfoque para lograr un código seguro de bajo nivel es utilizar lenguajes seguros de alto nivel. Se considera que un lenguaje seguro está completamente definido por su manual de programación. [4] Cualquier error que pueda llevar a un comportamiento dependiente de la implementación en un lenguaje seguro será detectado en tiempo de compilación o conducirá a un comportamiento de error bien definido en tiempo de ejecución. En Java , si accede a una matriz fuera de los límites, se lanzará una excepción. Ejemplos de otros lenguajes seguros son C # , Haskell y Scala .
Ejecución defensiva de lenguajes inseguros
Durante la compilación de un lenguaje inseguro, se agregan comprobaciones en tiempo de ejecución al código de bajo nivel para detectar un comportamiento indefinido en el nivel de origen. Un ejemplo es el uso de canarios , que pueden terminar un programa al descubrir violaciones de límites. Una desventaja de utilizar comprobaciones en tiempo de ejecución, como la comprobación de límites, es que imponen una sobrecarga de rendimiento considerable.
La protección de la memoria , como el uso de pilas y / o montones no ejecutables, también puede verse como comprobaciones adicionales en tiempo de ejecución. Esto es utilizado por muchos sistemas operativos modernos.
Ejecución aislada de módulos
La idea general es identificar el código sensible a partir de los datos de la aplicación analizando el código fuente. Una vez hecho esto, los diferentes datos se separan y se colocan en diferentes módulos. Al asumir que cada módulo tiene control total sobre la información sensible que contiene, es posible especificar cuándo y cómo debe salir del módulo. Un ejemplo es un módulo criptográfico que puede evitar que las claves dejen el módulo sin cifrar.
Certificación de compilación
La compilación de certificación es la idea de producir un certificado durante la compilación del código fuente, utilizando la información de la semántica del lenguaje de programación de alto nivel. Este certificado debe adjuntarse al código compilado para proporcionar una forma de prueba al consumidor de que el código fuente se compiló de acuerdo con un cierto conjunto de reglas. El certificado se puede producir de diferentes formas, por ejemplo, mediante el código de prueba (PCC) o el lenguaje ensamblador mecanografiado (TAL).
Código de prueba
Los principales aspectos del CCP se pueden resumir en los siguientes pasos: [5]
- El proveedor proporciona un programa ejecutable con varias anotaciones producidas por un compilador certificador .
- El consumidor proporciona una condición de verificación, basada en una política de seguridad . Esto se envía al proveedor.
- El proveedor ejecuta la condición de verificación en un demostrador de teoremas para producir una prueba al consumidor de que el programa de hecho satisface la política de seguridad.
- Luego, el consumidor ejecuta la prueba en un comprobador de pruebas para verificar la validez de la prueba.
Un ejemplo de un compilador certificador es el compilador Touchstone , que proporciona una prueba formal de PCC de seguridad de tipo y memoria para programas implementados en Java.
Lenguaje ensamblador mecanografiado
TAL es aplicable a lenguajes de programación que utilizan un sistema de tipos . Después de la compilación, el código objeto llevará una anotación de tipo que puede ser verificada por un verificador de tipo normal. La anotación producida aquí es en muchos aspectos similar a las anotaciones proporcionadas por PCC, con algunas limitaciones. Sin embargo, TAL puede manejar cualquier política de seguridad que se exprese por las restricciones del tipo de sistema, que puede incluir seguridad de memoria y flujo de control, entre otros.
Seminarios
- Seminario Dagstuhl 03411 , Language-Based Security, 5 al 10 de octubre de 2003.
Referencias
- ^ "¿Podemos aprender de los incidentes de seguridad de SCADA?" (PDF) . www.oas.org . enisa.
- ^ "Falló el sistema de control de tráfico aéreo" . www.computerworld.com . Consultado el 12 de mayo de 2014 .
- ^ "Error de software contribuido al apagón" . www.securityfocus.com . Consultado el 11 de febrero de 2004 .
- ^ Pierce, Benjamin C. (2002). Tipos y lenguajes de programación . La prensa del MIT. ISBN 9780262162098.
- ^ Kozen, Dexter (1999). "Seguridad basada en el idioma" (PDF) . Universidad de Cornell. Cite journal requiere
|journal=
( ayuda )
Libros
- G. Barthe, B. Grégoire, T. Rezk, Compilación de certificados , 2008
- Brian Chess y Gary McGraw, Análisis estático para la seguridad , 2004.
Otras lecturas
- Dexter Kozen, seguridad basada en el lenguaje , Universidad de Cornell, 1999
- Pieter Agten et al., Desarrollos recientes en seguridad de software de bajo nivel , Universiteit Leuven
- Andrei Sabelfeld y Andrew C. Myers, Seguridad del flujo de información basada en el idioma
- Fred B. Schneider et al., A Language-Based Approach to Security , Carnegie Mellon University, 2000