Las páginas web normales pueden usar el objeto XMLHttpRequest para enviar y recibir datos de fuentes remotas. servidores, pero están limitados por la política del mismo origen. Las secuencias de comandos de contenido inician solicitudes en nombre del origen web en el que se insertó la secuencia de comandos del contenido y, por lo tanto, el también están sujetas a la política del mismo origen. (Los guiones de contenido están sujetos a la CORB) desde Chrome 73 y CORS desde Chrome 83). Los orígenes de las extensiones no son muy limitados: una secuencia de comandos que se ejecutan en la página en segundo plano o en la pestaña en primer plano de una extensión pueden comunicarse con servidores remotos fuera de su origen, siempre que la extensión solicite permisos de origen cruzado.
Origen de la extensión
Cada extensión en ejecución existe dentro de su propio origen de seguridad independiente. Si no solicitas
la extensión puede usar XMLHttpRequest para obtener recursos dentro de su instalación. Para
Por ejemplo, si una extensión contiene un archivo de configuración JSON llamado config.json
, en una
config_resources
, la extensión puede recuperar el contenido del archivo de la siguiente manera:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = handleStateChange; // Implemented elsewhere.
xhr.open("GET", chrome.extension.getURL('/config_resources/config.json'), true);
xhr.send();
Si la extensión intenta usar un origen de seguridad distinto de sí misma, por ejemplo, https://www.google.com, el navegador la rechaza, a menos que la extensión haya solicitado el origen cruzado adecuado permisos.
Cómo solicitar permisos de origen cruzado
Agregando hosts o patrones de coincidencia de host (o ambos) a la sección de permisos de la manifiesto, la extensión puede solicitar acceso a servidores remotos fuera de su origen.
{
"name": "My extension",
...
"permissions": [
"https://www.google.com/"
],
...
}
Los valores de permisos de origen cruzado pueden ser nombres de host completos, como los siguientes:
- "https://www.google.com/"
- "https://www.gmail.com/"
O pueden ser patrones de coincidencia, como los siguientes:
- "https://*.google.com/"
- "https://*/"
Un patrón de coincidencia de “https://*/” permite el acceso HTTPS a todos los dominios accesibles. Ten en cuenta que aquí debes son similares a los patrones de coincidencia de secuencias de comandos de contenido, pero cualquier información de ruta de acceso que siga host se ignora.
Además, ten en cuenta que el acceso se otorga tanto por el host como por el esquema. Si una extensión desea un servicio tanto seguro como acceso HTTP no seguro a un host determinado o a un conjunto de hosts, debe declarar los permisos por separado:
"permissions": [
"http://www.google.com/",
"https://www.google.com/"
]
Consideraciones de seguridad
Evita las vulnerabilidades de secuencias de comandos entre sitios
Al usar recursos recuperados a través de XMLHttpRequest, debes tener cuidado con tu página en segundo plano. serán víctimas de secuencias de comandos entre sitios. Específicamente, evita usar APIs peligrosas, como a continuación:
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// WARNING! Might be evaluating an evil script!
var resp = eval("(" + xhr.responseText + ")");
...
}
}
xhr.send();
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// WARNING! Might be injecting a malicious script!
document.getElementById("resp").innerHTML = xhr.responseText;
...
}
}
xhr.send();
En su lugar, usa APIs más seguras que no ejecuten secuencias de comandos:
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// JSON.parse does not evaluate the attacker's scripts.
var resp = JSON.parse(xhr.responseText);
}
}
xhr.send();
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// innerText does not let the attacker inject HTML elements.
document.getElementById("resp").innerText = xhr.responseText;
}
}
xhr.send();
Cómo limitar el acceso a la secuencia de comandos de contenido a solicitudes de origen cruzado
Cuando realice solicitudes de origen cruzado en nombre de una secuencia de comandos de contenido, tenga cuidado de protegerse de páginas web maliciosas que podrían intentar suplantar la identidad de una secuencia de comandos de contenido. En particular, no permitir secuencias de comandos de contenido para solicitar una URL arbitraria.
Considera un ejemplo en el que una extensión realiza una solicitud de origen cruzado para permitir que una secuencia de comandos de contenido descubrir el precio de un artículo. Un enfoque (no seguro) consiste en que la secuencia de comandos del contenido especifique el recurso exacto que recuperará la página en segundo plano
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.contentScriptQuery == 'fetchUrl') {
// WARNING: SECURITY PROBLEM - a malicious web page may abuse
// the message handler to get access to arbitrary cross-origin
// resources.
fetch(request.url)
.then(response => response.text())
.then(text => sendResponse(text))
.catch(error => ...)
return true; // Will respond asynchronously.
}
});
chrome.runtime.sendMessage(
{contentScriptQuery: 'fetchUrl',
url: 'https://another-site.com/price-query?itemId=' +
encodeURIComponent(request.itemId)},
response => parsePrice(response.text()));
En el enfoque anterior, la secuencia de comandos del contenido puede pedirle a la extensión que recupere cualquier URL que la extensión tiene acceso. Una página web maliciosa puede falsificar esos mensajes y engañar a la extensión para que lo que otorga acceso a recursos de origen cruzado.
En su lugar, diseña controladores de mensajes que limiten los recursos que se pueden recuperar. A continuación, solo
La secuencia de comandos del contenido proporciona itemId
, y no la URL completa.
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.contentScriptQuery == 'queryPrice') {
var url = 'https://another-site.com/price-query?itemId=' +
encodeURIComponent(request.itemId);
fetch(url)
.then(response => response.text())
.then(text => parsePrice(text))
.then(price => sendResponse(price))
.catch(error => ...)
return true; // Will respond asynchronously.
}
});
chrome.runtime.sendMessage(
{contentScriptQuery: 'queryPrice', itemId: 12345},
price => ...);
Usa HTTPS en lugar de HTTP
Además, ten especial cuidado con los recursos recuperados a través de HTTP. Si la extensión se usa en un hostil, un atacante de red (también conocido como "man-in-the-middle") podría modificar la respuesta y, potencialmente, atacar tu extensión. En su lugar, usa HTTPS siempre que sea posible.
Ajusta la política de seguridad del contenido
Si modificas la Política de Seguridad del Contenido predeterminada para apps o extensiones y agregas una
content_security_policy
a tu manifiesto, deberás asegurarte de que todos los hosts
que deseas conectar están permitidos. Aunque la política predeterminada no restringe
las conexiones a los hosts,
Ten cuidado cuando agregues de manera explícita las directivas connect-src
o default-src
.