CORS 和网络扩展

CORS and web extensions

我在 http://localhost:8080 where http://example.com 上设置了一个服务器,可以执行 POST 个请求:

'use strict';

const express = require('express');

const app = express();
const port = 8080;

// allowing CORS for example.com
app.use('/', function (req, res, next) {
    res.header('Access-Control-Allow-Origin', 'http://example.com');
    if (req.method === 'OPTIONS') {
        res.header('Access-Control-Allow-Methods', 'OPTIONS, POST');
        res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length');
        res.status(200).send();
    } else {
        next();
    }
});

// handling POST requests
app.use('/', function (req, res) {
    console.log('a client did a POST request');
    res.status(200);
});

app.listen(port, () => console.log ('server started on port ' + port));

工作正常:由于同源策略,我无法向 http://localhost:8080 from http://localhost:8081 发出 POST 请求。

然后我为 Firefox 编写了一个 Web 扩展,它将尝试从任何域向 http://localhost:8080 发出 POST 请求。

这是它的清单:

{
    "manifest_version" : 2,
    "name" : "aBasicExtension",
    "version" : "0.0.0",
    "content_scripts" : [
        {
            "matches" : ["<all_urls>"],
            "js" : ["content-script.js"]
        }
    ],
    "permissions" : ["*://*.localhost/*"]
}

及其content-script.js代码:

(() => {

    'use strict';

    const xhr = new XMLHttpRequest();

    xhr.open('POST', 'http://localhost:8080');
    xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');

    xhr.addEventListener('readystatechange', () => {
        if (xhr.readyState === XMLHttpRequest.DONE){
            if (xhr.status === 200) console.log('OK');
            else console.error('an error has occured : ' + xhr.status);
        }
    });

    xhr.send(JSON.stringify({dataName: 'some data here'}));

})();

我不明白的是它有效。扩展程序向 http://localhost:8080 发出请求并且 Firefox 没有阻止它,因为清单允许它,但是服务器 (http://locahost:8080) 没有同意

简短版本:CORS 是一种用于控制浏览器而非服务器行为的协议。并且您使用插件 permissions 设置绕过了同源策略和对 CORS 的需求。

如果您查看 CORS 代码,您会发现它不会执行任何拒绝请求的操作;它只是在响应中设置 headers 。那些 headers 将指示浏览器客户端是否可以读取响应,但无论如何都会发送响应。

这一事实可能会被某些强制执行 CORS 预检的请求所掩盖。在那种情况下,浏览器首先发送一个特殊的 OPTIONS 请求,并且附加到该响应的 headers 可以阻止浏览器发送真正的请求。这是一种 backwards-compatibility 机制,并不适用于所有请求。 (有关详细说明,请参阅 this answer。)

这就是您的示例中发生的情况。您的 POST 属于需要在 CORS 下进行预检的类型。所以在常规版本中,浏览器发送预检,看到响应headers,而不会发送真正的请求。但如果它是另一种 POST 它会直接发送请求,服务器会执行它。

在插件版本中,您在 permissions 设置中明确允许该域。这个bypasses the Same Origin Policy:

The extra privileges include: XMLHttpRequest and fetch access to those origins without cross-origin restrictions (even for requests made from content scripts).

所以在这种情况下不需要预检,直接发送请求。

如果你想拒绝服务器上来自特定域的请求(或更普遍地防止 CSRF),还有其他设置。它们是什么取决于您的网络框架。