¿Qué tan fácil es hackear JavaScript (en un navegador)?

37

Mi pregunta tiene que ver con la seguridad de JavaScript.

Imagine un sistema de autenticación en el que utiliza un marco de JavaScript como Backbone o AngularJS , y necesita puntos finales seguros. Eso no es un problema, ya que el servidor siempre tiene la última palabra y verificará si está autorizado para hacer lo que quiera.

Pero, ¿y si necesita un poco de seguridad sin involucrar al servidor? ¿Es eso posible?

Por ejemplo, supongamos que tiene un sistema de enrutamiento del lado del cliente y desea proteger una ruta concreta para los usuarios registrados. Entonces haces ping al servidor preguntando si puedes visitar rutas protegidas y continúas. El problema es que cuando hace ping al servidor, almacena la respuesta en una variable, por lo que la próxima vez que vaya a una ruta privada, comprobará si ya ha iniciado sesión (no hay ping al servidor), y dependiendo en la respuesta irá o no.

¿Qué tan fácil es para un usuario modificar esa variable y obtener acceso?

Mi conocimiento de seguridad (y JavaScript) no es excelente. Pero si una variable no está en el alcance global y está en la parte privada de un patrón de módulo que solo tiene captadores pero no establecedores, incluso en ese caso, ¿puede hackear la cosa?

Jesus Rodriguez
fuente
29
Todas estas largas respuestas. En resumen, para responder a su encabezado, "muy pirateable". Para responder a sus primeras 2 preguntas en línea, "sin servidor, perdió seguridad" y "No". Para responder a la 3ra, "Muy fácil", y finalmente, "Sí, con facilidad". Ahí tienes. Todas las preguntas respondidas. : P
SpYk3HH
Primer ejemplo, vea mi publicación de blog sobre agregar jQuery a cualquier página web. spyk3lc.blogspot.com/2013/05/… Ahora, joven padawan, ve y pruébalo. Vea cuán fácil es agregar jQuery a cualquier sitio que aún no lo tenga, luego use jquery para manipular fácilmente cualquier parte de la vista sin largas filas de javascript. ¡Auge!
SpYk3HH
77
Una vez, al registrarse para un nombre de dominio, el número de teléfono era un campo obligatorio, pero eso solo se aplicaba en el JavaScript, no en el lado del servidor. Entonces, lo deshabilité redefiniendo una función (usando Firebug), y ¡voilà! No tienen mi número de teléfono.
Izkata
8
manipulate any part of the sight without long lines sitio vs vista
mplungjan
8
Los programadores web profesionales deben hacer lo correcto. Por favor. Es más vergonzoso que una gramática nazi :) plus.google.com/u/0/+MonaNomura/posts/h9ywDhfEYxT
mplungjan

Respuestas:

26

Lea la respuesta de Joachim antes de leer esta. Cubre las razones generales detrás de la vulnerabilidad del lado del cliente. Ahora, para una sugerencia de cómo solucionar este problema ...

Un esquema seguro para la comunicación cliente-servidor sin tener que autenticarse manualmente con el servidor en cada solicitud:

Estás todavía dejar que el servidor tiene la última palabra, y el servidor todavía tiene que validar todo lo que el cliente dice, pero sucede de forma transparente.

Asuma el protocolo HTTPS para prevenir ataques de hombre en el medio (MITMA).

  • El cliente se da la mano con el servidor por primera vez, y el servidor genera una clave pública para el cliente y mantiene una privada utilizando un esquema de cifrado asimétrico. El cliente almacena la clave "pública" del servidor en el almacenamiento local, encriptada con una contraseña segura que no guarda en ningún lugar.

  • El cliente ahora está fuera de línea. El cliente quiere realizar acciones confiables. El cliente ingresa su contraseña y toma la clave pública del servidor.

  • El cliente ahora realiza acciones basadas en su conocimiento de esos datos, y el cliente cifra cada acción que realiza con la clave pública del servidor para ese cliente .

  • Cuando el cliente está en línea, el cliente envía su ID de cliente y todas las acciones que el cliente realizó se envían al servidor cifradas con la clave pública del servidor.

  • El servidor descifra las acciones y, si están en el formato correcto, confía en que se originaron en el cliente.

Nota:

  • No puede almacenar la contraseña del cliente en ningún lado, de lo contrario, un atacante podría obtener la clave y firmar las acciones como si fueran propias. La seguridad de este esquema se basa únicamente en la integridad de la clave que el servidor genera para el cliente. El cliente aún necesitaría ser autenticado con el servidor cuando solicite esa clave.

  • De hecho, todavía confía en el servidor por seguridad y no en el cliente. Cada acción que realice el cliente debe validarla en el servidor.

  • Es posible ejecutar scripts externos en trabajadores web . Tenga en cuenta que cada solicitud JSONP que tiene ahora es un problema de seguridad mucho mayor. Debe proteger la clave a toda costa. Una vez que lo pierde, un atacante puede suplantar al usuario.

  • Esto satisface su demanda de que no se realice "ping al servidor". Un atacante no puede simplemente imitar una solicitud HTTP con datos falsificados si no conoce la clave.

  • La respuesta de Joachim sigue siendo correcta . Todavía está, de hecho, realizando toda la autenticación en el servidor . Lo único que guardó aquí es la necesidad de validar la contraseña con el servidor cada vez. Ahora solo necesita involucrar al servidor cuando desea confirmar o extraer datos actualizados. Todo lo que hicimos aquí es guardar una clave confiable en el lado del cliente y hacer que el cliente la vuelva a validar.

  • Este es un esquema bastante común para aplicaciones de una sola página (por ejemplo, con AngularJS).

  • Llamo a la clave pública del servidor "pública" por lo que eso significa en esquemas como RSA , pero de hecho es información confidencial en el esquema y debe protegerse.

  • No guardaría la contraseña en ningún lugar de la memoria. Haría que el usuario envíe su contraseña 'fuera de línea' cada vez que comience a ejecutar el código fuera de línea.

  • No utilice su propia criptografía : use una biblioteca conocida como la de Stanford para la autenticación.

  • Sigue este consejo tal como está . Antes de implementar este tipo de autenticación en una aplicación crítica del mundo real, consulte a un experto en seguridad . Este es un problema grave que es doloroso y fácil de equivocar.

Es crítico que ningún otro script tenga acceso a la página. Esto significa que solo permite scripts externos con trabajadores web. No puede confiar en ningún otro script externo que pueda interceptar su contraseña cuando el usuario la ingrese.

Use un campo de contraseña en línea prompty no uno si no está completamente seguro y no difiere su ejecución (es decir, no debería vivir hasta un punto donde los eventos tengan acceso a él, sino solo en el código de sincronización). Y en realidad no almacene la contraseña en una variable; de ​​nuevo, esto solo funciona si confía en que la computadora del usuario no está comprometida (aunque eso también es cierto para validar contra un servidor).

Me gustaría agregar nuevamente que todavía no confiamos en el cliente . No puedes confiar solo en el cliente, y creo que la respuesta de Joachim lo logra. Solo obtuvimos la conveniencia de no tener que hacer ping al servidor antes de comenzar a trabajar.

Material relacionado:

Benjamin Gruenbaum
fuente
3
"No utilice su propia criptografía" se aplica tanto a los protocolos como a las primitivas, si no más, porque si bien las personas generalmente no son lo suficientemente tontas como para hacer sus propias primitivas, piensan que pueden crear un protocolo que esté bien . Tampoco veo por qué es necesario RSA aquí. Dado que está utilizando HTTPS, ¿por qué no solo genera un secreto compartido de 16-32 bytes y requiere que se envíe con futuras solicitudes? Por supuesto, si haces esto, asegúrate de usar una verificación de igualdad invariable en el tiempo en la cadena ...
Reid
2
+1 @Reid Sí, acabo de tener una discusión sobre esto con un experto sobre esto recientemente. Basé el esquema primitivo aquí en un protocolo que aprendí (por Rabin IIRC), pero al menos hasta que pueda encontrar los detalles (y justificar, por ejemplo, por qué se necesita cifrado asimétrico en este caso). No use el esquema sugerido en esta respuesta sin consultar primero a un experto en seguridad . He visto este enfoque utilizado en varios lugares, pero en la práctica eso significa muy poco, en todo caso. También edité la respuesta para mejorar eso.
Benjamin Gruenbaum
Usar un aviso agrega poca seguridad. Un atacante puede anular el window.promptmétodo para interceptar la frase de paso o iniciar un mensaje propio (¡posiblemente dentro de un iframe!). Un campo de contraseña tiene un beneficio adicional: los caracteres no se muestran.
Rob W
@RobW Por supuesto, todo este esquema no tiene valor si la página se ve comprometida. Si el atacante tiene acceso para ejecutar JavaScript en la página, estamos jodidos. La idea detrás del aviso era que está bloqueando pero tienes razón, cualquier beneficio de seguridad que brinde es dudoso. Vea el último enlace en la lista.
Benjamin Gruenbaum
1
Ante todo. Impresionante respuesta, creo que no puedo pedir nada más. Por otro lado, quería hacer proyectos favoritos para mí (y, por supuesto, para quién quiere usarlo) y tal vez esto sea excesivo (o tal vez no). Quiero decir, no quiero hacer nada serio y me temo que no puedo hacer ese tipo de autenticación que explicaste, falta de conocimiento. Creo que rodaría un simple cheque 401 o, si no tengo ninguna solicitud, simplemente hago ping al servidor cada vez que accedo a ese tipo de rutas. El token de autenticación también es una opción (y tal vez lo más relacionado con lo que explicaste aquí AFAIK). Gracias :)
Jesus Rodriguez
89

Es simple: cualquier mecanismo de seguridad que se base en que el cliente haga solo lo que usted le dice puede verse comprometido cuando un atacante tiene control sobre el cliente.

Usted puede tener los controles de seguridad en el cliente, pero sólo para efectivamente actuar como un "caché" (para evitar hacer una cara de ida y vuelta al servidor si el cliente ya sabe que la respuesta será "no").

Si desea mantener la información de un conjunto de usuarios, asegúrese de que el cliente de esos usuarios nunca obtenga esa información. Si envía esos "datos secretos" junto con instrucciones "pero no los muestre", será trivial deshabilitar el código que verifica esa solicitud.

Como puede ver, esta respuesta realmente no menciona ninguna especificación de JavaScript / navegador. Esto se debe a que este concepto es el mismo, sin importar cuál sea su cliente. Realmente no importa que sea un cliente pesado (aplicación cliente / servidor tradicional), una aplicación web de la vieja escuela o una aplicación de una sola página con JavaScript extenso del lado del cliente.

Una vez que sus datos salen del servidor, debe suponer que un atacante tiene acceso completo a ellos.

Joachim Sauer
fuente
Gracias. Sería encantador si puedes explicarlo un poco más, mi conocimiento de seguridad no es tan bueno y la única parte que entiendo es que estoy equivocado: P. Cuando hablamos de almacenamiento en caché, ¿es solo almacenar en caché cuando el servidor dice que no? Quiero decir, si el servidor dijo que sí una vez, no puedes almacenar eso en caché, ¿verdad?
Jesus Rodriguez
3
Solo me gustaría agregar que es posible acceder a las variables de cierre en el navegador (como en el patrón de módulo que mencionó), especialmente con un depurador :)
Benjamin Gruenbaum
2
@BenjaminGruenbaum: siéntase libre de ampliar eso en una respuesta que se centre en el lado JS de las cosas. Solo di aquí una descripción general de alto nivel y agnóstica de la tecnología. ¡Una respuesta centrada en JS en sí misma también sería buena!
Joachim Sauer
1
En resumen, si alguna información / ruta / lo que sea accesible depende de una variable de JavaScript, eso no será seguro en ningún caso porque puede cambiar fácilmente esa variable para decir lo que necesita para acceder a esas cosas privadas.
Jesus Rodriguez
44
@JoachimSauer Creo que eso pasaría por alto el punto de esta pregunta que acertó muy bien. Independientemente de las medidas de seguridad que tome el cliente, es posible comprometer la comunicación . Puede crear sistemas de autenticación muy sólidos en JavaScript, pero no vale nada en el momento en que inserta la comunicación en un servidor que otros clientes tratan como una fuente de verdad. Todo el código fuente se envía al lado del cliente, un atacante inteligente puede leerlo e imitar una solicitud HTTP al servidor. Uno debe tratar todo lo que se origina en el cliente como no confiable a menos que se valide en el servidor.
Benjamin Gruenbaum
10

Hay un dicho en la comunidad de jugadores: " El cliente está en manos del enemigo". Cualquier código que se ejecute fuera de un área segura como el servidor es vulnerable. En el escenario más básico, vulnerable a no ejecutarse en primer lugar. Es la decisión del cliente ejecutar realmente su" código de seguridad "y el usuario puede simplemente "optar por salir". Mientras que con el código nativo tiene al menos una ofuscación automática en el ensamblaje y una capa adicional de protección por el hecho de que un atacante necesita ser un buen programador para manipular eso, JS normalmente viene sin obstrucciones y como texto sin formato. Todo lo que necesita para organizar un ataque son herramientas primitivas como un servidor proxy y un editor de texto. El atacante aún necesitará un cierto nivel de educación sobre programación, pero es mucho más fácil modificar un script usando cualquier editor de texto que inyectar código en un ejecutable.

nvoigt
fuente
La asamblea no es una ofuscación, y quien cree lo contrario nunca ha jugado un juego de guerra
miniBill
@miniBill: Si bien un atacante con experiencia moderada no tendrá problemas con el código ensamblado, proporcionaría un filtro de paso alto muy básico. (Por desgracia, esto es académico, ya que eliminar los script kiddies proporciona un aumento mínimo de seguridad para el escenario del OP)
Piskvor
1

No se trata de hackear JavaScript. Si quisiera atacar su aplicación, usaría un proxy privado que me permitiría capturar, modificar y reproducir el tráfico. Su esquema de seguridad propuesto no parece tener ninguna protección contra eso.

Mark E. Haase
fuente
0

Hablando específicamente sobre Angular:

Proteger una ruta del lado del cliente simplemente no existe. Incluso si 'oculta' un botón para esa ruta, el usuario siempre puede escribirlo, Angular se quejará, pero el cliente puede codificarlo.

En algún momento, su controlador tendrá que pedirle a su servidor los datos necesarios para representar la vista, si el usuario no está autorizado para acceder a esos datos, no los recibirán ya que ha protegido estos datos en el lado del servidor , y su aplicación Angular debería manejar el 401 adecuadamente.

Si está tratando de proteger los datos sin procesar, no puede ser del lado del cliente, si solo desea que un usuario en particular solo pueda ver datos comunes de cierta manera, cree las 'vistas' de datos en el servidor en lugar de enviar datos brutos para el cliente y reorganizarlos (de todos modos, ya debería hacerlo por razones de rendimiento).

Nota al margen: nunca debe haber nada sensible integrado en las plantillas de vista que solicite Angular, simplemente no lo haga. Si por alguna extraña razón existe, debe proteger esas plantillas de vista en el lado del servidor, tal como lo haría si estuviera haciendo la representación del lado del servidor.

marca
fuente