El flujo de información en un contexto teórico de la información es la transferencia de información de una variable a otra variable en un proceso determinado . No todos los flujos pueden ser deseables; por ejemplo, un sistema no debería filtrar ninguna información confidencial (parcial o no) a observadores públicos, ya que es una violación de la privacidad a nivel individual o podría causar pérdidas importantes a nivel corporativo.
En los últimos años, proteger los datos manipulados por los sistemas informáticos ha sido un desafío. Hoy en día existen varios métodos para limitar la divulgación de información, como las listas de control de acceso , los cortafuegos y la criptografía . Sin embargo, aunque estos métodos imponen límites a la información que se publica por un sistema, no ofrecen garantías sobre la propagación de la información . [1] Por ejemplo, las listas de control de acceso de los sistemas de archivos impiden el acceso no autorizado a los archivos, pero no controlan cómo se utilizan los datos posteriormente. De manera similar, la criptografía proporciona un medio para intercambiar información de forma privada a través de un canal no seguro, pero no ofrece garantías sobre la confidencialidad de los datos una vez que se descifran.
En el análisis de flujo de información de bajo nivel, a cada variable se le suele asignar un nivel de seguridad. El modelo básico comprende dos niveles distintos: bajo y alto, es decir, información observable públicamente e información secreta, respectivamente. Para garantizar la confidencialidad, no se debe permitir el flujo de información de variables altas a variables bajas. Por otro lado, para garantizar la integridad, se deben restringir los flujos a variables altas. [1]
De manera más general, los niveles de seguridad pueden verse como una red en la que la información fluye sólo hacia arriba. [2]
Por ejemplo, considerando dos niveles de seguridad y (bajo y alto), si , fluye desde a , desde a , y a estaría permitido, mientras que fluye desde a no lo estaría. [3]
A lo largo de este artículo se utiliza la siguiente notación:
¿Dónde y son los únicos dos niveles de seguridad en la red que se están considerando?
Los flujos de información se pueden dividir en dos categorías principales. La más simple es el flujo explícito, en el que un secreto se filtra explícitamente a una variable observable públicamente. En el siguiente ejemplo, el secreto de la variable h fluye hacia la variable observable públicamente l .
variedad l, hyo:= yo
Los demás flujos entran en la categoría de canal lateral . Por ejemplo, en el ataque de sincronización o en el ataque de análisis de potencia , el sistema filtra información a través, respectivamente, del tiempo o la potencia que se necesita para realizar una acción en función de un valor secreto.
En el siguiente ejemplo, el atacante puede deducir si el valor de h es uno o no por el tiempo que tarda el programa en finalizar:
var l, h si h = 1 entonces (*hacer algún trabajo que consume mucho tiempo*)yo := 0
Otro flujo de canal lateral es el flujo de información implícito, que consiste en la fuga de información a través del flujo de control del programa. El siguiente programa revela (implícitamente) el valor de la variable secreta h a la variable l . En este caso, como la variable h es booleana, se revelan todos los bits de la variable h (al final del programa, l será 3 si h es verdadero y 42 en caso contrario).
var l, h si h = verdadero entonces yo:= 3demás yo:= 42
La no interferencia es una política que obliga a un atacante a no poder distinguir dos cálculos de sus resultados si solo varían en sus entradas secretas. Sin embargo, esta política es demasiado estricta para ser utilizable en programas realistas. [4] El ejemplo clásico es un programa de verificación de contraseñas que, para ser útil, necesita revelar cierta información secreta: si la contraseña ingresada es correcta o no (nótese que la información que un atacante aprende en caso de que el programa rechace la contraseña es que la contraseña intentada no es la válida).
Un mecanismo para el control del flujo de información es aquel que hace cumplir las políticas de flujo de información. Se han propuesto varios métodos para hacer cumplir las políticas de flujo de información. Se han empleado mecanismos de tiempo de ejecución que etiquetan los datos con etiquetas de flujo de información a nivel del sistema operativo y a nivel del lenguaje de programación. También se han desarrollado análisis estáticos de programas que garantizan que los flujos de información dentro de los programas se ajusten a las políticas.
Se han desarrollado tanto análisis estáticos como dinámicos para los lenguajes de programación actuales. Sin embargo, las técnicas de análisis dinámico no pueden observar todas las rutas de ejecución y, por lo tanto, no pueden ser a la vez fiables y precisas. Para garantizar la no interferencia, finalizan las ejecuciones que podrían liberar información confidencial [5] o ignoran las actualizaciones que podrían filtrar información. [6]
Una forma destacada de hacer cumplir las políticas de flujo de información en un programa es mediante un sistema de tipos de seguridad: es decir, un sistema de tipos que hace cumplir las propiedades de seguridad. En un sistema de tipos tan sólido, si un programa realiza una verificación de tipos, cumple con la política de flujo y, por lo tanto, no contiene flujos de información inadecuados.
En un lenguaje de programación ampliado con un sistema de tipos de seguridad, cada expresión lleva un tipo (como booleano o entero) y una etiqueta de seguridad.
A continuación se muestra un sistema de tipos de seguridad simple de [1] que impone la no interferencia. La notación significa que la expresión tiene tipo . De manera similar, significa que el comando se puede tipificar en el contexto de seguridad .
Los comandos bien escritos incluyen, por ejemplo,
Por el contrario, el programa
está mal tipificado, ya que revelará el valor de la variable en .
Tenga en cuenta que la regla es una regla de subsunción, lo que significa que cualquier comando que sea de tipo de seguridad también puede ser . Por ejemplo, puede ser tanto como . Esto se llama polimorfismo en la teoría de tipos . De manera similar, el tipo de una expresión que satisface puede ser tanto como según y respectivamente.
Como se ha demostrado anteriormente, la política de no interferencia es demasiado estricta para su aplicación en la mayoría de las aplicaciones del mundo real. [7] Por lo tanto, se han ideado varios enfoques para permitir la divulgación controlada de información. Estos enfoques se denominan desclasificación de la información.
Una desclasificación robusta requiere que un atacante activo no pueda manipular el sistema para conocer más secretos de los que los atacantes pasivos ya conocen. [4]
Los constructos de desclasificación de información se pueden clasificar en cuatro dimensiones ortogonales: qué información se divulga, quién está autorizado a acceder a la información, dónde se divulga la información y cuándo se divulga la información. [4]
Una política de desclasificación controla qué información (parcial o no) puede divulgarse a una variable observable públicamente.
El siguiente ejemplo de código muestra una construcción de desclasificación de. [8] En este código, el programador permite explícitamente que el valor de la variable h fluya hacia la variable observable públicamente l .
var l, h si l = 1 entonces l := desclasificar (h)
Una política de desclasificación de quién controla qué personas pueden acceder a una determinada información. Este tipo de política se ha implementado en el compilador Jif. [9]
El siguiente ejemplo permite a Bob compartir su secreto contenido en la variable b con Alice a través de la variable comúnmente accesible ab .
var ab (* {Alice, Bob} *) var b (* {Bob} *) si ab = 1 entonces ab := desclasificar (b, {Alice, Bob}) (* {Alice, Bob} *)
Una política de desclasificación regula dónde se puede publicar la información, por ejemplo, controlando en qué líneas del código fuente se puede publicar la información.
El siguiente ejemplo hace uso de la construcción de flujo propuesta en [10] . Esta construcción toma una política de flujo (en este caso, se permite que las variables en H fluyan hacia las variables en L) y un comando, que se ejecuta bajo la política de flujo dada.
var l, h flujo H L en yo:= yo
Una política de desclasificación regula cuándo se puede divulgar la información. Las políticas de este tipo se pueden utilizar para verificar programas que implementan, por ejemplo, la divulgación controlada de información secreta después de un pago, o secretos encriptados que no deben divulgarse en un tiempo determinado dado un poder computacional polinomial.
Un flujo implícito ocurre cuando un código cuya ejecución condicional se basa en información privada actualiza una variable pública. Esto es especialmente problemático cuando se consideran múltiples ejecuciones, ya que un atacante podría aprovechar la variable pública para inferir información privada al observar cómo cambia su valor con el tiempo o con la entrada.
El enfoque ingenuo consiste en hacer cumplir la propiedad de confidencialidad en todas las variables cuyo valor se ve afectado por otras variables. Este método conduce a una filtración parcial de información debido a que en algunas instancias de la aplicación una variable es baja y en otras alta. [ aclarar ]
"No se admiten actualizaciones sensibles" detiene el programa siempre que una variable High afecta el valor de una variable Low. Dado que simplemente busca expresiones en las que podría producirse una fuga de información, sin tener en cuenta el contexto, puede detener un programa que, a pesar de tener una fuga de información potencial, en realidad nunca la fuga.
En el siguiente ejemplo, x es alto e y es bajo.
var x, yy := falsoSi x = verdadero entonces y := verdaderodevuelve verdadero
En este caso el programa se detendría ya que, sintácticamente hablando, utiliza el valor de una variable High para cambiar una variable Low, a pesar de que el programa nunca filtra información.
La actualización permisiva introduce una clase de seguridad adicional P que identificará las variables que pueden filtrar información. Cuando una variable High afecta el valor de una variable Low, esta última se etiqueta como P. Si una variable etiquetada como P afecta a una variable Low, el programa se detendrá. Para evitar la detención, las variables Low y P se deben convertir a High mediante una función de privatización para garantizar que no se produzcan fugas de información. En instancias posteriores, el programa se ejecutará sin interrupciones.
La inferencia de privatización extiende la actualización permisiva para aplicar automáticamente la función de privatización a cualquier variable que pueda filtrar información. Este método se debe utilizar durante las pruebas, donde convertirá la mayoría de las variables. Una vez que el programa pasa a producción, se debe utilizar la actualización permisiva para detener el programa en caso de una fuga de información y las funciones de privatización se pueden actualizar para evitar fugas posteriores.
Más allá de las aplicaciones a los lenguajes de programación, las teorías de control del flujo de información se han aplicado a sistemas operativos, [11] sistemas distribuidos, [12] y computación en la nube. [13] [14]