¿Por qué no existe una política del mismo origen para WebSockets? ¿Por qué puedo conectarme a ws: // localhost?

84

Me gustaría usar WebSockets para la comunicación entre procesos para mi aplicación (Daemon <-> WebGUI y Daemon <-> FatClient, etc.). Durante la prueba, intenté conectarme a mi servidor de socket web que se ejecuta localmente (ws: // localhost: 1234) a través del cliente JavaScript WebSocket en websocket.org ( http://www.websocket.org/echo.html ).

Mi pregunta ahora es:
¿Por qué es esto posible? ¿No hay una política de origen cruzado implementada en los navegadores (aquí: FF29 en Linux)?

Estoy preguntando porque si websocket.org fuera malvado, podría intentar comunicarse con mi servidor WS local y redirigir cada mensaje que recibe de localhost a cualquier otro servidor:

Local WebSocket Server Navegador Evil Web Server
en ws: // localhost: 1234 en http: //evil.tld
        | | |
        | | ------ [GET /] ---------> |
        | | <----- [HTML + EvilJS] ---- |
        | <------ [conectar ws: // ..] ---- | |
        | <---- [alguna comunicación] -> | |
        | | ---- [delantero malvado] ----> |
        | | |

No he probado todo el caso de uso, pero la conexión a ws: // localhost del JS entregado por websocket.org definitivamente funciona.

binwiederhier
fuente
2
websocket.org no debería ser malvado, los sockets web pueden serlo;)
kuldeep.kamboj

Respuestas:

51

Para abordar el "¿Por qué?" En parte, la razón por la que los navegadores no aplican la Política del mismo origen (de la cual CORS es una relajación) para WebSockets a diferencia de las llamadas AJAX, es porque los WebSockets se introdujeron después de que se estableció el valor de las solicitudes de origen cruzado y porque ' no están sujetos a SOP para empezar, la razón histórica de las verificaciones del lado del cliente CORS no se aplica.

Para AJAX, en los días de una Política de origen único general, los servidores nunca esperaban que un navegador autenticado enviara una solicitud desde un dominio 1 diferente , por lo que no necesitaban asegurarse de que la solicitud provenía de una ubicación confiable 2 , solo marque el cookie de sesión. Las relajaciones posteriores como CORS tuvieron que tener verificaciones del lado del cliente para evitar exponer las aplicaciones existentes a abusos al violar esta suposición (efectivamente haciendo un ataque CSRF ).

Si la Web se estuviera inventando hoy, sabiendo lo que sabemos ahora, no se requerirían ni SOP ni CORS para AJAX y es posible que toda la validación quede en manos del servidor.

WebSockets, al ser una tecnología más nueva, está diseñado para admitir escenarios de dominio cruzado desde el principio. Cualquiera que escriba la lógica del servidor debe ser consciente de la posibilidad de solicitudes de origen cruzado y realizar la validación necesaria, sin la necesidad de tomar precauciones estrictas en el lado del navegador al estilo CORS.


1 Esta es una simplificación. Las solicitudes de recursos GET de origen cruzado (incluidas las etiquetas <img>, <link> y <script>) y las solicitudes POST de envío de formularios siempre se permitieron como una característica fundamental de la Web. Hoy en día, las llamadas AJAX de origen cruzado cuyas solicitudes tienen las mismas propiedades también están permitidas y se conocen como solicitudes simples de origen cruzado . Sin embargo, no se permite acceder a los datos devueltos de tales solicitudes en código a menos que lo permitan explícitamente los encabezados CORS del servidor. Además, estas solicitudes POST "simples" son la razón principal por la que los tokens anti-CSRF son necesarios para que los servidores se protejan de sitios web maliciosos.

2 De hecho, ni siquiera estaba disponible una forma segura de verificar el origen de la solicitud, ya que el Refererencabezado se puede falsificar, por ejemplo, mediante una vulnerabilidad de redireccionamiento abierto. Esto también muestra cuán mal se entendían las vulnerabilidades CSRF en ese entonces.

staafl
fuente
6
De hecho, esto responde a la pregunta, así que +1. Pero, para que conste, estoy totalmente en desacuerdo con este razonamiento. Predigo que, como resultado de esta decisión de diseño, una cantidad significativa de sitios que utilizan WebSockets no podrán validar el Originencabezado y, como resultado, filtrarán datos privados de usuarios a sitios de terceros. Los clientes que verificaban el Access-Control-Allow-Originencabezado, como lo hacen antes de permitir el acceso de JS a las respuestas a cualquier otra solicitud HTTP de origen cruzado en la web, habrían sido una forma sencilla de prevenir toda esta clase de ataque (secuestro de WebSocket entre sitios). Muy tarde ahora.
Ajedi32
3
Me inclino a estar de acuerdo, el cambio de diseño es esencialmente pasar de un enfoque basado en listas blancas a uno en listas negras, lo cual es arriesgado. Punto justo.
staafl
42

oberstet respondió a la pregunta . ¡Gracias! Lamentablemente, no puedo marcarlo como "correcto" porque era un comentario. El navegador envía el encabezado "origen" que puede ser verificado por la aplicación.

En Java [1]:

@Anular
public void onOpen (WebSocket clientSocket, ClientHandshake apretón de manos) {
    String clientOrigin = handshake.getFieldValue ("origen");

    if (clientOrigin == null ||! clientOrigin.equals (WEBSOCKET_ALLOWED_ORIGIN_HEADER)) {
        logger.log (Level.WARNING, "El cliente no envió el encabezado de origen correcto:" + clientOrigin);        

        clientSocket.close ();
        regreso;
    }

    // ...
}

[1] usando https://github.com/TooTallNate/Java-WebSocket

binwiederhier
fuente
OWASP menciona verificar el encabezado de Origen (y potencialmente el Referer) en su hoja de trucos de CSRF como el primer y más importante paso, pero también recomiendan ir un paso más allá e implementar una defensa específica de CSRF. En el caso de WebSockets, esto podría ser agregar un token anti-falsificación XSRF al WS URI como parámetro de consulta y validarlo en el lado del servidor después de la verificación de origen.
Kevin Secrist
18

Los WebSockets pueden comunicarse entre dominios y no están limitados por el SOP (Política del mismo origen).

El mismo problema de seguridad que describió puede ocurrir sin WebSockets.

El malvado JS puede:

  • Cree una etiqueta de secuencia de comandos / imagen con una URL a evil.tld y coloque los datos en la cadena de consulta.
  • Cree una etiqueta de formulario, coloque los datos en los campos e invoque la acción "enviar" del formulario, haciendo un HTTP POST, que puede ser de dominio cruzado. AJAX está limitado por el SOP, pero HTTP POST normal no lo está. Verifique el problema de seguridad web XSRF.

Si algo inyecta javascript en su página, o obtiene javascript malicioso, su seguridad ya está rota.

vtortola
fuente
1
No me preocupa el malvado JS. Sé que esto siempre es posible. Lo que realmente me preocupa es la ruptura del navegador: cualquier sitio web ahora puede comunicarse con un socket WS vinculado localmente y robar datos de allí.
binwiederhier
53
SOP / CORS no se aplica a WebSocket, pero los navegadores enviarán un originencabezado que contiene el nombre de host del servidor que sirvió el HTML con el JS que abrió la conexión WebSocket. Un servidor WebSocket puede restringir el acceso marcando origin.
oberstet
Esto no responde a la pregunta. La pregunta era por qué una página web de un dominio diferente puede acceder a un WebSocket local. En el escenario de OP, no hay nada que "inyecte javascript en su página", ese es un escenario diferente. Sin WebSocket, una página web remota no podría leer recursos en localhost, porque eso es precisamente lo que evita el SOP.
Sleske