首页 > 解决方案 > 同源提取导致空比较(HTTP CORS)

问题描述

一个简单的同源fetch("fetch.xml")在此处失败并显示控制台错误消息

CORS 策略已阻止从源“null”获取“http://127.0.0.1:8000/fetch.xml”的访问权限:“Access-Control-Allow-Origin”标头的值为“http://” 127.0.0.1:8000' 不等于提供的原点。让服务器发送带有有效值的标头,或者,如果不透明的响应满足您的需求,请将请求的模式设置为“no-cors”以获取禁用 CORS 的资源。

相应的网络标头是

General
Request URL: http://127.0.0.1:8000/fetch.xml
Referrer Policy: origin

Response Headers
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Origin: http://127.0.0.1:8000
Access-Control-Max-Age: 86400
Content-Security-Policy: sandbox allow-scripts
Content-Type: text/xml
Date: Sun, 16 Aug 2020 06:20:43 GMT
Referrer-Policy: origin
Server: SimpleHTTP/0.6 Python/3.8.5

Request Headers
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
Cache-Control: no-cache
Connection: keep-alive
DNT: 1
Host: 127.0.0.1:8000
Origin: null
Pragma: no-cache
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36

如您所见,浏览器(此处为 Chrome)显示 Origin = null,尽管来源是同一个来源。我也可以使用真实域而不是 ip 地址来重现它,根据浏览器,提取的来源始终为空。

如果我将 fetch 语句更改fetch("fetch.xml", { mode: "same-origin" })为浏览器仍然假定 null 是原点,但错误消息更改为

Fetch API 无法加载http://127.0.0.1:8000/fetch.xml。请求模式为“同源”,但 URL 的来源与请求来源 null 不同。

如何让浏览器使用正确的来源而不是 fetch 语句为空?如您所见,我使用了推荐人策略。我不希望Access-Control-Allow-Origin包含 null 或 *,因为这似乎太开放了(基本上禁用了 CORS)。

备注:我没有通过file://URL 访问文件!这是一个真正的 Web 服务器,并且来源仍然为空!所以这个问题与重复的stackoverflow链接到的不同。



最小复制示例

如果您想快速测试它,简单的服务器包括所有内容(只需使用 python3 运行它):

#!/usr/bin/env python3

from http.server import HTTPServer, HTTPStatus, SimpleHTTPRequestHandler, test
import sys
from urllib.parse import urlparse
import os

class TestRequestHandler (SimpleHTTPRequestHandler):
    def do_GET(self):
        """Serve a GET request."""
        url = urlparse(self.path)
        if url.path == '/' or url.path == '/index.html': # index.html
            f = self.send_head('text/html')
            self.wfile.write('''<!DOCTYPE html>
                            <html>
                            <head>
                                <title>Test</title>
                                <script type="text/javascript" src="control.js"></script>
                            </head>
                            <body>
                            </body>
                            </html>'''.encode('utf-8'))
        elif url.path == '/control.js': # control.js will try to fetch the fetch.xml
            f = self.send_head('application/javascript')
            self.wfile.write('''function init() {
                    fetch("fetch.xml", { mode: "same-origin" }).then(r => r.text()).then(t => console.log(t));
                }
                window.addEventListener("load", init);'''.encode('utf-8'))
        elif url.path == '/fetch.xml': # fetch.xml
            f = self.send_head('text/xml')
            self.wfile.write('''<?xml version="1.0" encoding="UTF-8"?>
                            <root>
                                <test>Hi</test>
                            </root>'''.encode('utf-8'))


    def send_head(self, ctype):
        self.send_response(HTTPStatus.OK)
        self.send_header('Content-Type', ctype)
        self.send_header('Access-Control-Allow-Origin', 'http://127.0.0.1:8000')
        self.send_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.send_header('Access-Control-Max-Age', 86400)
        self.send_header('Referrer-Policy', 'origin')
        self.send_header('Content-Security-Policy', 'sandbox allow-scripts')
        SimpleHTTPRequestHandler.end_headers(self)

if __name__ == '__main__':
    test(TestRequestHandler, HTTPServer, port=int(sys.argv[1]) if len(sys.argv) > 1 else 8000)

如果您更喜欢将其放在您自己的服务器上,并使用不同的文件:

索引.html

<!DOCTYPE html>
<html>
<head>
    <title>Test</title>
    <script type="text/javascript" src="control.js"></script>
</head>
<body>
</body>
</html>

控制.js

function init() {
    fetch("fetch.xml", { mode: "same-origin" }).then(r => r.text()).then(t => console.log(t));
}
window.addEventListener("load", init);

获取.xml

<?xml version="1.0" encoding="UTF-8"?>
<root><test>Hello</test></root>

服务器应设置为发送以下标头(调整 ip/域):

Access-Control-Allow-Origin: http://127.0.0.1:8000
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Max-Age: 86400
Referrer-Policy: origin
Content-Security-Policy: sandbox allow-scripts

标签: javascripthttpcors

解决方案


这是你的问题:

Content-Security-Policy: sandbox allow-scripts

请参阅文档

您没有启用此指令:

允许同源

允许将内容视为来自其正常来源。如果未使用此关键字,则嵌入的内容将被视为来自唯一来源。

......所以http://localhost:8000/http://localhost:8000/fetch.xml被视为不同的,独特的起源。


推荐阅读