Conceptos básicos de seguridad web: restricciones del mismo origen

La piedra angular de la seguridad del navegador es la "política del mismo origen " . Muchos desarrolladores lo saben, pero no del todo.

Descripción general

significado

En 1995, Netscape introdujo la política del mismo origen en los navegadores. Actualmente, todos los navegadores implementan esta política.

Inicialmente, su significado es que la cookie establecida por la página web A no puede ser abierta por la página web B a menos que las dos páginas web tengan el mismo origen. El llamado "mismo origen" se refiere a "tres similitudes".

  • El acuerdo es el mismo.

  • Mismo nombre de dominio

  • El puerto es el mismo (esto se puede ignorar, consulte los detalles a continuación)

Por ejemplo, para http://www.example.com/dir/page.htmlesta URL, el protocolo es http://, el nombre de dominio es www.example.com, el puerto es 80(se puede omitir el puerto predeterminado) y su homología es la siguiente.

  • http://www.example.com/dir2/other.html:Mismo origen

  • http://example.com/dir/other.html: Diferentes fuentes (diferentes nombres de dominio)

  • http://v2.www.example.com/dir/other.html: Diferentes fuentes (diferentes nombres de dominio)

  • http://www.example.com:81/dir/other.html: Diferentes fuentes (diferentes puertos)

  • https://www.example.com/dir/page.html: Diferentes fuentes (diferentes protocolos)

Tenga en cuenta que el estándar estipula que las URL con diferentes puertos no tienen el mismo origen (por ejemplo, el puerto 8000 y el puerto 8001 no tienen el mismo origen), pero los navegadores no cumplen con esta regla. De hecho, diferentes puertos del mismo dominio pueden leer las cookies de los demás.

Objetivo

El propósito de la política del mismo origen es garantizar la seguridad de la información del usuario y evitar que sitios web maliciosos roben datos.

Imagine esta situación: el sitio web A es un banco. Después de que el usuario inicia sesión, el sitio web A establece una cookie en la máquina del usuario, que contiene información privada. Después de que el usuario abandona el sitio web A, vuelve a visitar el sitio web B. Si no hay restricción de origen, el sitio web B puede leer las cookies del sitio web A y luego se filtra la privacidad. Lo que es aún más aterrador es que las cookies se utilizan a menudo para guardar el estado de inicio de sesión del usuario. Si el usuario no cierra la sesión, otros sitios web pueden hacerse pasar por él y hacer lo que quieran. Porque el navegador también estipula que el envío de formularios no está restringido por la política del mismo origen.

Se puede ver que es necesaria la misma política de origen; de lo contrario, las cookies se pueden compartir y Internet no será seguro en absoluto.

rango límite

Con el desarrollo de Internet, las políticas del mismo origen se han vuelto cada vez más estrictas. Actualmente, existen tres conductas que se restringen si no son del mismo origen.

(1) No se pueden leer las cookies, LocalStorage e IndexedDB de páginas web no originales.

(2) No se puede acceder al DOM de páginas web no homogéneas.

(3) No se puede enviar una solicitud AJAX a una dirección no original (se puede enviar, pero el navegador se negará a aceptar la respuesta).

Además, se pueden obtener objetos de otras ventanas mediante scripts JavaScript window. Si se trata de una página web no original, actualmente se permite que una ventana acceda a windownueve propiedades y cuatro métodos de objetos de otras páginas web.

  • ventana.cerrada

  • Marcos de ventana

  • ventana.longitud

  • ventana.ubicación

  • abridor de ventana

  • ventana.padre

  • ventana.yo

  • ventana.arriba

  • ventana.ventana

  • ventana.desenfoque()

  • ventana.cerrar()

  • ventana.enfoque()

  • ventana.postMessage()

Entre los nueve atributos anteriores, sólo uno window.locationes legible y escribible, y los otros ocho son todos de sólo lectura. Además, incluso si locationel objeto no es del mismo origen, solo se permiten llamar location.replace()a métodos y escribir propiedades.location.href

Aunque estas restricciones son necesarias, a veces resultan inconvenientes y comprometen fines legítimos. A continuación se explica cómo eludir las restricciones anteriores.

Galleta

Una cookie es una pequeña porción de información escrita por el servidor en el navegador, que solo puede ser compartida por páginas web con el mismo origen. Si los nombres de dominio de primer nivel de dos páginas web son iguales pero solo los nombres de dominio de segundo nivel son diferentes, el navegador permite document.domaincompartir cookies a través de la configuración.

Por ejemplo, si la URL de la página web A es http://w1.example.com/a.htmly la URL de la página web B es http://w2.example.com/b.html, siempre que la configuración sea la misma document.domain, las dos páginas web pueden compartir cookies. Porque el navegador document.domaincomprueba si tiene el mismo origen mediante atributos.

// Ambas páginas web deben configurarse 
document.domain = 'example.com';

Tenga en cuenta que ambas páginas web A y B deben establecer document.domainatributos para lograr el mismo origen. Debido a que document.domainel puerto se restablecerá al configurar null, si solo configura una página web document.domain, los puertos de las dos URL serán diferentes y no se logrará el propósito de la homología.

Ahora, la página web A establece una cookie mediante un script.

document.cookie = "prueba1=hola";

La página web B puede leer esta cookie.

var allCookie = documento.cookie;

Tenga en cuenta que este método solo se aplica a ventanas de cookies y iframe. LocalStorage e IndexedDB no pueden eludir la política del mismo origen a través de este método. En su lugar, utilice la API PostMessage que se presenta a continuación.

Además, el servidor también puede especificar el nombre de dominio de la cookie como nombre de dominio de primer nivel al configurar la cookie, por ejemplo example.com.

Establecer-Cookie: clave=valor; dominio=ejemplo.com; ruta=/

En este caso, tanto el nombre de dominio de segundo nivel como el nombre de dominio de tercer nivel pueden leer esta cookie sin ninguna configuración.

iframe y comunicación multiventana

iframeLos elementos se pueden incrustar en otras páginas web dentro de la página web actual. Cada iframeelemento forma su propia ventana, es decir, tiene su propio windowobjeto. iframeEl script en la ventana puede obtener la ventana principal y la ventana secundaria. Sin embargo, la ventana principal y la ventana secundaria solo pueden comunicarse si tienen el mismo origen; si cruzan dominios, no pueden obtener el DOM de la otra parte.

Por ejemplo, si la ventana principal ejecuta el siguiente comando, si iframela ventana no proviene de la misma fuente, se informará un error.

document 
.getElementById("myIFrame") 
.contentWindow 
.document 
// DOMException no detectada: se bloqueó el acceso de un marco a un marco de origen cruzado.

En el comando anterior, la ventana principal quiere obtener el DOM de la ventana secundaria, pero se informa un error debido a dominios cruzados.

Viceversa, la ventana secundaria también informará un error al obtener el DOM de la ventana principal.

window.parent.document.body 
// Informe de error

Esta situación se aplica no solo a iframelas ventanas, sino también a window.openlas ventanas abiertas por métodos: mientras crucen dominios, no habrá comunicación entre la ventana principal y la ventana secundaria.

Si los nombres de dominio de primer nivel de las dos ventanas son iguales pero solo los nombres de dominio de segundo nivel son diferentes, entonces configurando los atributos introducidos en la sección anterior, puede document.domaineludir la política del mismo origen y obtener el DOM.

Para sitios web con orígenes completamente diferentes, actualmente existen dos métodos para resolver el problema de comunicación de las ventanas entre dominios.

  • identificador de fragmento

  • API de mensajería entre documentos (mensajería entre documentos)

identificador de fragmento

El identificador de fragmento se refiere a #la parte que sigue al número de URL, como por http://example.com/x.html#fragmentejemplo #fragment. Si simplemente cambia el identificador del fragmento, la página no se actualizará.

La ventana principal puede escribir información en el identificador de fragmento de la ventana secundaria.

var src = origenURL + '#' + datos; 
document.getElementById('myIFrame').src = src;

En el código anterior, la ventana principal escribe la información que se transmitirá en el identificador de fragmento de la ventana iframe.

Las ventanas secundarias hashchangereciben notificaciones escuchando eventos.

window.onhashchange = checkMessage; 
​function
checkMessage() { 
  var mensaje = window.location.hash; 
  //... 
}

Asimismo, una ventana secundaria puede cambiar el identificador de fragmento de la ventana principal.

parent.ubicación.href = objetivo + '#' + hash;

ventana.postMessage()

El método anterior es un crack. Para resolver este problema, HTML5 introduce una nueva API: API de mensajería entre documentos (mensajería entre documentos).

Esta API windowagrega un nuevo window.postMessagemétodo al objeto que permite la comunicación entre ventanas, independientemente de si las dos ventanas tienen el mismo origen. Por ejemplo, si la ventana principal envía un mensaje aaa.coma la ventana secundaria bbb.com, postMessagesimplemente llame al método.

// La ventana principal abre una ventana secundaria 
var popup = window.open('http://bbb.com', 'title'); 
// La ventana principal envía un mensaje a la ventana secundaria 
popup.postMessage('Hola mundo !', 'http://bbb.com');

postMessageEl primer parámetro del método es el contenido de información específico y el segundo parámetro es el origen de la ventana que recibe el mensaje, es decir, "protocolo + nombre de dominio + puerto". También se puede configurar *para que los nombres de dominio no estén restringidos y se envíen a todas las ventanas.

La forma en que la ventana secundaria envía un mensaje a la ventana principal es similar.

//La ventana secundaria envía un mensaje a la ventana principal 
window.opener.postMessage('Encantado de verte', 'http://aaa.com');

Tanto la ventana principal como la secundaria pueden messageescuchar los mensajes de cada una a través de eventos.

// Tanto la ventana principal como la secundaria pueden usar el siguiente código, 
// para escuchar los mensajes 
window.addEventListener('message', function (e) { 
  console.log(e.data); 
},false);

messageEl parámetro del evento es el objeto del evento event, que proporciona las siguientes tres propiedades.

  • event.source: Ventana para enviar mensajes

  • event.origin: URL a la que se envía el mensaje

  • event.data: Contenido del mensaje

En el siguiente ejemplo, la ventana secundaria event.sourcehace referencia a la ventana principal a través de propiedades y luego envía un mensaje.

window.addEventListener('mensaje', recibirMensaje); 
function recibirMensaje(evento) { 
  event.source.postMessage('¡Qué bueno verte!', '*'); 
}

Hay varias cosas a tener en cuenta sobre el código anterior. En primer lugar, receiveMessageno hay ninguna fuente de información filtrada en la función y se procesará la información enviada desde cualquier URL. En segundo lugar, postMessagela URL de la ventana de destino especificada en el método es un asterisco, lo que indica que la información se puede enviar a cualquier URL. En términos generales, estos dos métodos no se recomiendan porque no son lo suficientemente seguros y pueden explotarse de forma maliciosa.

event.originLas propiedades pueden filtrar los mensajes no enviados a esta ventana.

window.addEventListener('mensaje', recibirMensaje); 
función recibirMensaje(evento) { 
  if (evento.origen!== 'http://aaa.com') retorno; 
  if (event.data === 'Hola mundo') { 
    event.source.postMessage('Hola', event.origin); 
  } más { 
    console.log(event.data); 
  } 
}

Almacenamiento local

A través de esto window.postMessage, también es posible leer y escribir el Almacenamiento Local de otras ventanas.

A continuación se muestra un ejemplo en el que la ventana principal se escribe en una ventana secundaria de iframe localStorage.

ventana.onmessage = función(e) { 
  if (e.origin!== 'http://bbb.com') { 
    retorno; 
  } 
  var carga útil = JSON.parse(e.data); 
  localStorage.setItem(carga útil.clave, JSON.stringify(carga útil.datos)); 
};

En el código anterior, la ventana secundaria escribe el mensaje enviado por la ventana principal en su propio LocalStorage.

El código para enviar mensajes desde la ventana principal es el siguiente.

var win = document.getElementsByTagName('iframe')[0].contentWindow; 
var obj = { nombre: 'Jack' }; 
win.postMessage ( 
  JSON.stringify ({clave: 'almacenamiento', datos: obj}), 
  'http://bbb.com' 
);

El código de la versión mejorada de la ventana secundaria para recibir mensajes es el siguiente.

ventana.onmessage = función(e) { 
  if (e.origin!== 'http://bbb.com') retorno; 
  carga útil var = JSON.parse(e.data); 
  switch (carga útil.método) { 
    caso 'conjunto': 
      localStorage.setItem(carga útil.clave, JSON.stringify(carga útil.datos)); 
      romper; 
    caso 'obtener': 
      var padre = ventana.padre; 
      var datos = localStorage.getItem(payload.key); 
      parent.postMessage(datos, 'http://aaa.com'); 
      romper; 
    caso 'eliminar': 
      localStorage.removeItem(payload.key); 
      romper; 
  } 
};

La versión mejorada del código de envío de mensajes para la ventana principal es la siguiente.

var win = document.getElementsByTagName('iframe')[0].contentWindow; 
var obj = { nombre: 'Jack' }; 
// 存入对象
win.postMessage( 
  JSON.stringify({clave: 'almacenamiento', método: 'set', datos: obj}), 
  'http://bbb.com' 
); 
// 读取对象
win.postMessage( 
  JSON.stringify({clave: 'almacenamiento', método: "get"}), 
  "*" 
); 
ventana.onmessage = función(e) { 
  if (e.origin!= 'http://aaa.com') retorno; 
  console.log(JSON.parse(e.data).nombre); 
};

AJAX

La política del mismo origen estipula que las solicitudes AJAX solo se pueden enviar a URL con el mismo origen; de lo contrario, se informará un error.

Además de configurar un servidor proxy (el navegador solicita el mismo servidor de origen, que luego solicita servicios externos), existen tres formas de eludir esta restricción.

  • JSONP

  • WebSocket

  • CORS

JSONP

JSONP es un método común para la comunicación entre orígenes entre servidores y clientes. La característica más importante es que es simple y fácil de usar, no hay problemas de compatibilidad, todos los navegadores antiguos lo admiten y la modificación del lado del servidor es muy pequeña.

Así es como se hace.

En el primer paso, la página web agrega un <script>elemento y solicita un script al servidor, que no está restringido por la política del mismo origen y se puede solicitar en todos los dominios.

<script src="http://api.foo.com?callback=bar"></script>

Tenga en cuenta que la URL del script solicitado tiene un callbackparámetro ( ?callback=bar), que se utiliza para indicarle al servidor el nombre de la función de devolución de llamada del cliente ( bar).

En el segundo paso, después de recibir la solicitud, el servidor concatena una cadena, coloca los datos JSON en el nombre de la función y los devuelve como una cadena ( bar({...})).

En el tercer paso, el cliente analizará la cadena devuelta por el servidor como un código, porque el navegador cree que este es el <script>contenido del script solicitado por la etiqueta. En este momento, siempre que el cliente defina bar()la función, puede obtener los datos JSON devueltos por el servidor en el cuerpo de la función.

Veamos un ejemplo a continuación. Primero, la página web inserta dinámicamente <script>un elemento que realiza una solicitud a la URL entre dominios.

función addScriptTag(src) { 
  var script = document.createElement('script'); 
  script.setAttribute('tipo', 'texto/javascript'); 
  script.src = src; 
  documento.body.appendChild(guión); 
} 
foo({}) 
ventana.onload = función () { 
  addScriptTag('http://example.com/ip?callback=foo'); 
} 
​function
foo(data) { 
  console.log('Su dirección IP pública es: ' + data.ip); 
};

El código anterior realiza una solicitud al servidor agregando <script>elementos dinámicamente example.com. Tenga en cuenta que la cadena de consulta de esta solicitud tiene un callbackparámetro que especifica el nombre de la función de devolución de llamada, que es necesaria para JSONP.

Después de que el servidor reciba esta solicitud, colocará los datos en la posición del parámetro de la función de devolución de llamada y los devolverá.

foo({ 
  'ip': '8.8.8.8' 
});

Dado que <script>el script solicitado por el elemento se ejecuta directamente como código. En este momento, siempre que el navegador defina foola función, la función se llamará inmediatamente. Los datos JSON como parámetros se tratan como objetos JavaScript en lugar de cadenas, evitando así el JSON.parsepaso de uso.

WebSocket

WebSocket es un protocolo de comunicación que utiliza ws://(sin cifrar) y wss://(cifrado) como prefijos de protocolo. Este protocolo no implementa una política del mismo origen y se puede realizar comunicación entre orígenes a través de él siempre que el servidor lo admita.

A continuación se muestra un ejemplo de la información del encabezado de una solicitud WebSocket emitida por el navegador (tomada de Wikipedia ).

GET /chat HTTP/1.1 
Host: server.example.com 
Actualización: websocket 
Conexión: Actualización 
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== 
Sec-WebSocket-Protocol: chat, superchat 
Sec-WebSocket-Version: 13 
Origen: http:// ejemplo.com

En el código anterior, hay un campo que Originindica el origen de la solicitud (origen), es decir, el nombre de dominio desde el que se envió.

Es precisamente por Origineste campo que WebSocket no implementa la misma política de origen. Porque el servidor puede determinar si permite esta comunicación en función de este campo. Si el nombre de dominio está en la lista blanca, el servidor responderá de la siguiente manera.

HTTP/1.1 101 Protocolos de conmutación 
Actualización: 
conexión websocket: Actualización 
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= 
Sec-WebSocket-Protocol: chat

CORS

CORS es la abreviatura de Cross-Origin Resource Sharing. Es un estándar del W3C y es la solución fundamental para solicitudes AJAX de origen cruzado. En comparación con JSONP, que solo puede enviar GETsolicitudes, CORS permite cualquier tipo de solicitud.

El próximo capítulo presentará en detalle cómo completar solicitudes AJAX de origen cruzado a través de CORS.

Link de referencia

Supongo que te gusta

Origin blog.csdn.net/qtttgeq/article/details/130758513
Recomendado
Clasificación