Curso MIT "Seguridad de sistemas informáticos". Lección 11: Ur / Lenguaje de programación web, Parte 3

Instituto de Tecnología de Massachusetts. Conferencia Curso # 6.858. "Seguridad de los sistemas informáticos". Nikolai Zeldovich, James Mickens. Año 2014


Computer Systems Security es un curso sobre el desarrollo e implementación de sistemas informáticos seguros. Las conferencias cubren modelos de amenazas, ataques que comprometen la seguridad y técnicas de seguridad basadas en trabajos científicos recientes. Los temas incluyen seguridad del sistema operativo (SO), características, gestión del flujo de información, seguridad del idioma, protocolos de red, seguridad de hardware y seguridad de aplicaciones web.

Lección 1: "Introducción: modelos de amenaza" Parte 1 / Parte 2 / Parte 3
Lección 2: "Control de ataques de hackers" Parte 1 / Parte 2 / Parte 3
Lección 3: “Desbordamientos del búfer: exploits y protección” Parte 1 / Parte 2 / Parte 3
Lección 4: “Separación de privilegios” Parte 1 / Parte 2 / Parte 3
Lección 5: “¿De dónde vienen los sistemas de seguridad?” Parte 1 / Parte 2
Lección 6: “Oportunidades” Parte 1 / Parte 2 / Parte 3
Lección 7: “Sandbox de cliente nativo” Parte 1 / Parte 2 / Parte 3
Lección 8: "Modelo de seguridad de red" Parte 1 / Parte 2 / Parte 3
Lección 9: "Seguridad de aplicaciones web" Parte 1 / Parte 2 / Parte 3
Lección 10: “Ejecución simbólica” Parte 1 / Parte 2 / Parte 3
Lección 11: "Ur / Lenguaje de programación web" Parte 1 / Parte 2 / Parte 3

Lo último que debemos hacer y que será muy instructivo es cambiar este código indicando el módulo de sala allí, y luego intentar acceder a la tabla de sala, como en el ejemplo anterior. Esta opción no está permitida porque no está permitida.



Esto sería como poder leer y escribir campos de clases privadas en Java. De hecho, recibimos un mensaje bastante simple, básicamente diciendo que tenemos una variable no relacionada aquí, una expresión desconocida para el parámetro room.



Podríamos mencionar este módulo adicional, que creamos solo por diversión.



Pero entonces serán tablas diferentes. Podemos acceder fácilmente a él. Por lo tanto, lo dividiré en dos partes, comenzaremos simplemente llamando al método room y luego haremos algo ligeramente diferente para leer sus elementos.



Muestre una lista de resultados, y no al revés, esto es más o menos equivalente a cómo funcionaba el programa anteriormente, excepto por el uso de varios tipos de datos. Veamos que viene de esto. Ahora volvemos a nuestro chat en la sala 1 e ingresamos cualquier mensaje en la línea. Verá que todos se muestran sin errores.



Es decir, ahora tenemos esta encapsulación, por lo que puede pensar en la estructura de esta sala como una biblioteca, pero no necesita preocuparse por eso.

Hay varios lugares que pueden dañar la invariancia interna de un sistema. Tal vez desee que después de agregar el mensaje, nunca desaparezca. Todo esto está en las revistas. Esta estructura proporciona tales propiedades independientemente de otro código, como aquel en el que está escrito el módulo de la sala de chat.

Estudiante: entonces cambió la definición de la sala, ¿qué pasaría con la tabla de la base de datos en este caso?

Profesor: debe ejecutar manualmente el comando alterar tabla si desea conservar los datos antiguos. Pero cuando se inicia la aplicación, consulta el directorio de la base de datos del sistema y verifica que el esquema sigue siendo el esperado. Entonces obtendrá un error estático. Espero que esto te dé una idea de lo que debes cambiar en la base de datos.

Estudiante: ¿ pero no eliminará automáticamente la base de datos o algo así?

Profesor: espero que no. No creo que el compilador deba hacer esto. Puedes imaginar personalizar el compilador para comprender la evolución de la base de datos. Creo que necesita escribir comandos de alterar tabla para comenzar, porque el compilador no hace esto ahora.



Ahora hablemos sobre la falsificación de solicitudes entre sitios y su prevención. De hecho, antes de hacer esto, veamos el código en esta página. Tenemos un formulario HTML tradicional que se genera aquí. Y, por supuesto, no hay protección contra la falsificación de solicitudes entre sitios, y creo que es bueno. Porque, según tengo entendido, el problema de falsificar las solicitudes entre sitios es el contexto implícito que la aplicación envía con cada solicitud.

Supongamos que hay algún tipo de atacante que no conoce su contexto implícito. Digamos que su contraseña se almacena en una cookie, por un ejemplo muy simple. Y cuando el atacante lo engaña al hacer clic en el enlace a la aplicación que necesita, el navegador envía automáticamente un contexto implícito y obliga a la aplicación a hacer lo que el atacante no pudo hacer directamente.



En este caso, no existe un contexto implícito, por lo que no hay riesgo de falsificar las solicitudes entre sitios. ¿Alguien quiere desafiar esta característica del sistema antes de continuar? Puede ser muy informativo para mí. Si no, entonces agregue un contexto implícito aquí. En este caso, el sistema tomará automáticamente las contramedidas correctas, según el análisis del programa, que comprende que ahora hay un contexto implícito.

Ahora insertaré cookies aquí. Como otro ejemplo de encapsulación de módulos, de hecho, pondré aquí un sistema completo de autenticación de usuarios en el que tenemos cuentas de usuario y tipos abstractos de identificadores y contraseñas. Por lo tanto, no puede simplemente crear el valor de ninguno de estos tipos de datos directamente. Tendrá que pasar por algún método aprobado para construir valores de estos tipos.

Voy a colocar la mesa justo en la firma. Y también le impondré una restricción, diciendo que la clave es el formulario de identificación.



Pero el hecho es que en esta tabla de usuario, la ID y la contraseña son tipos de datos abstractos. Por lo tanto, el código no puede ver la contraseña y no puede generar consistentemente todos los identificadores y probarlos en esta tabla. Debido a que usa un tipo abstracto, por lo que es imposible establecer cómo se ve la ID y es imposible encontrar una contraseña. Solo vienen de esta tabla, y estos son tokens opacos.

Pero podríamos permitir que sean entradas de aguas residuales. Es posible que desee permitir una dirección de conversión entre cadenas y estos tipos. Ahora haré algo aquí, principalmente los detalles, y no intentaré explicarlos. Pero esto es como declarar que puede convertir cadenas a ID. Para aquellos familiarizados con Haskell, esta es una clase de tipo instantánea, este permiso para convertir cadenas en ID.



No vamos a aplicar otro permiso, porque no queremos poder volver a convertir la ID en otra cosa. Hagamos lo mismo con la contraseña. Queremos poder leer la contraseña del usuario, pero no vamos a aceptar la contraseña y convertirla en una cadena que nos dirá que el usuario ha entrado en el chat.

Por lo tanto, otras partes del código podrán aceptar la contraseña del usuario, convertirla a este tipo y transportarla al módulo de usuario para su verificación. Pero lo que no pueden hacer es consultar la tabla de usuario y obtener todas las contraseñas en un formulario del que puedan extraer su expresión de texto.

Entonces podemos tener un método de inicio de sesión que acepte estos dos componentes, ID y contraseña, y simplemente funcione como un efecto secundario, que en realidad se indica en el código. También necesitaremos una forma de averiguar qué usuario está registrado. Este es el código que ejecuta la transacción que crea la ID.



El primer paso es simplemente copiar esta definición. Y luego viene una sorpresa. Resulta que los ID de usuario y las contraseñas son cadenas, pero esta circunstancia no se revelará fuera del módulo.



Ahora vamos a crear cookies. Las cookies son otra cosa que está integrada en el lenguaje. De hecho, actúan como variables globales mutables que tienen una copia para cada cliente que utiliza su aplicación.

Por lo tanto, vamos a crear una cookie que para cada usuario simplemente almacenará una copia de los mismos dos campos que tenemos aquí.



Estas cookies son privadas para este módulo. Otras partes del código no podrán leer cookies porque simplemente no tienen este campo privado. Por lo tanto, nadie podrá ver directamente el ID y la contraseña guardados para este usuario. Pero se guardarán al ver varias páginas, como es el caso de las cookies comunes.

Ahora insertaré la función de inicio de sesión, que iniciará el proceso para verificar la base de datos y averiguar si este es realmente el par correcto de nombre de usuario y contraseña. Este proceso solo verificará si podemos encontrar una fila en la base de datos que contenga este ID de usuario y contraseña.

Si lo encontramos, entonces bueno, entonces este es el significado correcto. Solo guardemos esto en las cookies. Utilizamos un método que cambia el valor de una cookie. Y tenemos que poner algunas cosas aquí, por simplicidad diré que esta cookie no caduca. No quiero ejecutar SSL aquí, por lo que diré que en este caso no necesitamos seguridad y estableceré el parámetro Secure = false.

Pero si realmente le importa la seguridad, obviamente escribirá Secure = true. Si la verificación falla y el módulo señala un error, el programa se detendrá y dará una descripción de este error.



Finalmente, podemos crear esta función que indica qué usuario en particular ha iniciado sesión al recibir el valor de cookie actual. Este parámetro también se puede establecer en none si el usuario aún no ha iniciado sesión, en cuyo caso obtendremos otro mensaje de error. O podría ser algún tipo de registro del tipo exacto que utilizamos anteriormente. Así que solo lo copio aquí y ejecuto la misma verificación. Si esto funciona, simplemente devolvemos esa parte del registro de ID que acabamos de verificar en la base de datos.



Así que déjame ver esto. Iniciamos el compilador y verá el resultado en la pantalla.



Central para todos estos detalles de implementación. Pero fuera de este módulo, lo pensamos desde el punto de vista de la interfaz. Hay algunos tipos desconocidos de ID y contraseñas. Esta tabla de usuario expresa términos que le permiten convertir cadenas en identificadores y contraseñas, pero no al revés. Tenemos estos dos métodos para ingresar el inicio de sesión primero y hay una verificación de qué usuario ha iniciado sesión en esta etapa. ¿Tienes preguntas sobre esto?

Estudiante: ¿Necesita expandir la tabla de usuarios?

Profesor: Hago esto porque quiero usarlo más tarde como una clave externa, por lo que esta no es una razón importante. Entonces, estamos casi en el punto en que puedo mostrarle la protección CSRF en acción.

Para empezar, iniciar sesión en el sistema es bastante fácil de hacer. Bueno, ¿qué más podemos hacer en este punto del programa? Agreguemos aquí otra parte de la página que dice que aquí es donde ingresa el inicio de sesión. Aquí debe ingresar el nombre de usuario y la contraseña y luego hacer clic en el botón Enviar acción. Esta acción proporcionará una llamada a la función de inicio de sesión.



Definamos el inicio de sesión como una función que hace estas cosas. De hecho, es solo un contenedor para llamar a la función de inicio de sesión desde este módulo, en el que tomamos cada uno de los componentes y los convertimos de una cadena a un tipo abstracto.



Ella busca un error de lectura. El error significa que si el inicio de sesión no funciona, la operación se interrumpirá en este punto y la función volverá a la parte principal del programa.
Ahora podemos iniciar sesión. Probablemente querremos crear una cuenta que nos permita iniciar sesión, así que permítame crear un usuario con nombre de usuario ay contraseña a.





Ahora puedo iniciar sesión como usuario a, confíe en mi palabra. Tenemos un conjunto de cookies para registrar esta información, así que vayamos a la sala de chat y enviemos un mensaje, por ejemplo, asfasf. Verá que después de hacer clic en el botón Agregar, apareció en el chat.



De hecho, no hemos agregado ningún control de acceso aquí, por lo que no sucede nada especial aquí. Pero podemos comprobarlo.



Aquí hay cookies, pero el sistema ha determinado que no las utilizamos. Cuando enviamos este formulario, la cookie no es legible. Por lo tanto, hasta ahora no hay necesidad de agregar protección CSRF aquí. Entonces, ahora necesitamos agregar una forma de usar cookies y ver cómo se manifestará la protección.

Estudiante: ¿qué es el contenido de cookies?

Profesor: este es el contenido que espera recibir del código. En otras palabras, se declara que la cookie tiene el tipo de este registro: identificador y contraseña.



Entonces, esto es exactamente lo que está contenido allí en cierta forma serializada. Ahora usemos realmente cookies. Deberíamos ver esto, a pesar del hecho de que usaremos la cookie indirectamente, porque la vamos a usar en el módulo de sala, que no tiene nada que ver con las cookies. Pero llamaremos métodos de módulos personalizados que están indirectamente relacionados con el uso de cookies. Y entonces el sistema comprenderá que esto significa que dependemos de él.

Entonces, hagámoslo de manera muy simple y llamemos al método whoami. De hecho, solo voy a ignorarlo, o viceversa, dejar que haga algo. Decidamos que el usuario que creamos es realmente especial, y solo este usuario puede publicar cualquier cosa. Si este no es el caso, recibiremos un mensaje de error.



Agregaré una ID al módulo de usuario, y esto debería funcionar, ya que el tipo de ID admite la verificación de igualdad.



Ahora todo debería estar en orden, y podemos hacer más cosas con esta ID, lo que puede causar algunos problemas de seguridad.



Esto nos permite agregar una verificación de control de acceso, así que veamos cómo funciona y regresemos a la página principal.



Ahora estas cuatro letras "a" aparecieron en el chat.

En la consola de la interfaz, vemos que ahora el formulario ha recibido automáticamente el nombre de entrada oculto sig, que es la firma criptográfica de los valores de todas las cookies. Esta firma firmó cookies utilizando una clave que es secreta para el servidor. Y cuando el formulario está listo, la aplicación sabe, porque el compilador le contó sobre esto, que la aplicación debe verificar las firmas para el próximo conjunto de operaciones. Para esto, tenemos una operación de decir.



Estudiante: ¿las firmas tienen una especie de marca de tiempo?

Profesor: no, las firmas no tienen marcas de tiempo.

Estudiante: en este caso, si un atacante logra "espiar" estos datos, podría pretender ser un usuario, porque estas cookies nunca caducarán.

Profesor: sí, nunca caduca. Esto es algo que se puede cambiar simplemente cambiando la implementación del lenguaje sin cambiar las aplicaciones, y luego desplegándolo rápidamente. Pero ahora esto no está aquí. Y entiendo por qué sería útil agregar esto.

Estudiante: también puede solucionar esto simplemente poniendo una marca de tiempo en la firma.

Profesor: sí, tiene razón, puede cambiar la aplicación de manera tal que cambie intencionalmente los datos de las cookies con la suficiente frecuencia, es decir, haga que la firma caduque.
Estudiante: ¿puedes reasignar URL?

Profesor: sí, ¿qué reasignación te gustaría ver?

Estudiante: cualquiera, solo quiero ver cómo se hace esto.

Profesor: Entonces, el compilador asigna ... como vemos, llamamos a la función say, y esta llamada a la función se serializa como una forma específica de URL. Supongamos que no nos gusta este formulario.



Decidimos que íbamos a reescribir la URL, por así decirlo, dentro del módulo de sala, dentro de la demostración. Mejor pegarlo. Por lo tanto, queremos reasignar la url Demo / Room / say a Demo / Room / Speak.



Iniciamos el compilador y vamos a la pantalla principal de la aplicación. Veamos que pasa. Todo está en orden, también podemos ingresar cualquier mensaje de texto y aparecerán en la línea de chat. Se pueden usar caracteres impredecibles en estas reglas para reemplazar un prefijo por otro, y el compilador se asegurará de que cada función tenga un esquema de URL diferente, pero la URL se genera automáticamente de forma predeterminada.

Estudiante: mencionaste que HTML no es específico del compilador, es solo una biblioteca. ¿Hay otras bibliotecas para otros formatos?

Profesor: hay otras bibliotecas que no comprueban el tipo con la misma integridad funcional, pero, por ejemplo, hay una biblioteca para serializar y deserializar JSON, y una gran cantidad de formas automatizadas para administrar la estructura de tipos. De esta manera puede hacer cosas que no están integradas en el compilador.

Estudiante: supongamos que todavía queremos escribir en JavaScript, por ejemplo, para animar algunas cosas en la página ...

Profesor: déjame descargar la versión Ajax de esto, que responderá a tu pregunta. Esta versión tiene código del lado del cliente. Pasemos a una versión del programa llamada demo3. Ingresé los datos en la página principal en la línea de chat, y también aparecieron en la parte inferior del chat. Lo creas o no, esta vez el complemento funcionó usando una llamada Ajax. Esto se debe al valor del botón de etiqueta del botón. Tiene un atributo onclick que, cuando el usuario hace clic en el botón, todo este código debajo de la línea con este atributo funciona en el lado del cliente.

]

Pero este es el código Ur / Web, este no es el código JavaScript. El compilador traduce esto en JavaScript para usted y se asegura de que guarda las propiedades que deseamos para abstracciones en nuestra lista si el usuario no quiere jugar manualmente en el navegador.

Estudiante: Creo que hoy en día hay muchas bibliotecas que hacen cosas útiles, y en muchos casos cosas complejas, si quieres recodificar todo tú mismo. ¿Hay alguna forma de interactuar con JavaScript desde Ur / Web?

Profesor: sí, hay una interfaz de función externa que le permite asignar nombres de funciones Ur / Web a nombres de funciones y llamadas de JavaScript. Pero cuando usa la interfaz de una función externa, ya no puede usar todas estas funciones de construcción útiles. En este caso hay que tener mucho cuidado.

Debe comprender la implementación de algunas de estas abstracciones para no interferir con ellas. Mientras tenga este código, déjame mostrarte algo más.

Todavía tenemos la misma función de decir que antes. Pero ahora, en lugar de llamarlo por referencia, solo tomamos una llamada de función, que se rellena con argumentos que provienen del contexto del controlador onclick.



Simplemente envolvemos esta función dentro de la sintaxis rpc. Esto significa que esta es una función de llamada del lado del cliente, pero la llamada en sí se inicia en el servidor con acceso a la base de datos y otros recursos del servidor, y luego transfiere el resultado aquí.

, , JavaScript .
, . , , , .



, , , . , , .

, - , . GUI , , , , .

, , , . , DOM. , , .



, GUI, , , , .

, , .

: ?

: , , . , - .

: . , ?



: — . , . , , . , .

: — Ur?

: Ur — , , . , . .


.

Gracias por quedarte con nosotros. ¿Te gustan nuestros artículos? ¿Quieres ver más materiales interesantes? Apóyenos haciendo un pedido o recomendándolo a sus amigos, un descuento del 30% para los usuarios de Habr en un análogo único de servidores de nivel de entrada que inventamos para usted: toda la verdad sobre VPS (KVM) E5-2650 v4 (6 núcleos) 10GB DDR4 240GB SSD 1Gbps de $ 20 o cómo dividir el servidor? (las opciones están disponibles con RAID1 y RAID10, hasta 24 núcleos y hasta 40GB DDR4).

VPS (KVM) E5-2650 v4 (6 núcleos) 10GB DDR4 240GB SSD 1Gbps hasta diciembre de forma gratuita al pagar por un período de seis meses, puede ordenar aquí .

Dell R730xd 2 veces más barato? ¡Solo tenemos 2 x Intel Dodeca-Core Xeon E5-2650v4 128GB DDR4 6x480GB SSD 1Gbps 100 TV desde $ 249 en los Países Bajos y los Estados Unidos! Lea sobre Cómo construir un edificio de infraestructura. clase utilizando servidores Dell R730xd E5-2650 v4 que cuestan 9,000 euros por un centavo?

Source: https://habr.com/ru/post/es426001/


All Articles