同源下Safari中COOP与window.open无法正常工作问题排查
COOP设置为
same-origin时,Safari同源弹窗出现跨源行为问题 我将Cross-Origin-Opener-Policy(COOP)设置为same-origin,在同源环境下(当前页面为https://example.com/here)通过用户触发事件调用window.open打开弹窗:
// 在用户触发的事件中 const childWindow = window.open('https://example.com/there', 'child-window')
Chrome和Firefox中表现符合预期:父窗口能获取子窗口的location和closed状态,子窗口也能访问window.opener。
但桌面端Safari中行为却和跨源场景一致:
childWindow.closed // true
弹窗内执行:
window.opener // null
将COOP改为same-origin-allow-popups也无法解决问题。
这是预期行为?还是我理解有误?或是Safari的特殊实现?
补充说明:
将COOP设为unsafe-none能解决问题,但这显然不是合理方案,对吗?
系统信息
- macOS
12.4 - Safari
15.5(17613.2.7.1.8)
响应头信息
Summary URL: https://example.com/here Status: 200 Source: Service Worker Request Cache-Control: max-age=0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.5 Safari/605.1.15 Response Content-Type: text/html; charset=utf-8 Content-Security-Policy: upgrade-in-secure-requests;default-src 'self';script-src 'self' 'unsafe-eval' 'sha256-<hereIsAHash>' 'sha256-<hereIsAnotherHash>';child-src 'self';connect-src 'self' https://example.com wss://example.com;font-src 'self' data:;form-action 'self';frame-ancestors 'self';frame-src *;img-src 'self' data: blob:;manifest-src 'self';media-src 'self';object-src 'self';sandbox allow-same-origin allow-downloads allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-scripts allow-forms allow-modals allow-presentation;style-src 'self' 'unsafe-inline';worker-src 'self' blob:;base-uri 'self';script-src-attr 'none';upgrade-insecure-requests X-XSS-Protection: 0 Cross-Origin-Resource-Policy: cross-origin Content-Encoding: gzip Referrer-Policy: no-referrer Date: Fri, 18 Nov 2022 08:46:17 GMT Cross-Origin-Opener-Policy: same-origin X-DNS-Prefetch-Control: off X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN Vary: Accept-Encoding Server: nginx/1.21.6 x-download-options: noopen x-permitted-cross-domain-policies: none expect-ct: max-age=604800, enforce Strict-Transport-Security: max-age=15552000; includeSubDomains, max-age=31536000 origin-agent-cluster: ?1
Helmet配置(版本:helmet@6.0.0)
const self = '\'self\'' const data = 'data:' const blob = 'blob:' const unsafeEval = '\'unsafe-eval\'' const unsafeInline = '\'unsafe-inline\'' const connectSrc = ['https://example.com', 'wss://example.com'] const externalHostUrls = ['https://foobar.com'] export const helmetOptions = { crossOriginEmbedderPolicy: false, crossOriginOpenerPolicy: { policy: 'same-origin' }, crossOriginResourcePolicy: { policy: 'cross-origin' }, contentSecurityPolicy: { blockAllMixedContent: true, directives: { upgradeInSecureRequests: [], defaultSrc: [self], scriptSrc: [ self, // 我们使用哈希允许动态导入时的unsafeEval unsafeEval, `'sha256-${hashes[0]}'`, `'sha256-${hashes[1]}'` ], childSrc: [self], connectSrc: connectSrc.concat(externalHostUrls), fontSrc: [self, data], formAction: [self], frameAncestors: [self], frameSrc: ['*'], imgSrc: [self, data, blob].concat(externalHostUrls), manifestSrc: [self], mediaSrc: [self], objectSrc: [self], sandbox: [ 'allow-same-origin', 'allow-downloads', 'allow-orientation-lock', 'allow-pointer-lock', 'allow-popups', 'allow-popups-to-escape-sandbox', 'allow-scripts', 'allow-forms', 'allow-modals', 'allow-presentation' ], styleSrc: [self, unsafeInline], workerSrc: [self, blob] } }, strictTransportSecurity: { maxAge: 15552000, includeSubDomains: true, preload: false }, referrerPolicy: { policy: 'no-referrer' }, expectCt: { enforce: true, maxAge: 604800 }, frameguard: { action: 'sameorigin' }, dnsPrefetchControl: { allow: false }, permittedCrossDomainPolicies: { permittedPolicies: 'none' }, hidePoweredBy: true } }
问题原因与解决方案
这是Safari 15.x版本对COOP策略的特殊实现bug,并非你的配置错误或理解偏差。
核心原因
Safari在处理same-origin或same-origin-allow-popups的COOP策略时,会错误地将同源弹窗放入独立的浏览上下文组,导致父窗口与子窗口无法互相引用,表现出跨源隔离的行为。
可行解决方案
- 升级Safari版本:该问题在Safari 16及以上版本中已被修复,升级后同源弹窗的COOP策略行为会与Chrome、Firefox保持一致。
- 临时兼容方案:若无法强制用户升级,可通过以下方式兼容:
- 通过User-Agent判断Safari 15.x用户,临时将COOP设置为
unsafe-none(仅作为过渡方案,注意该配置会取消跨源隔离保护,存在安全风险); - 使用
postMessage替代直接的窗口引用交互,这种方式不受COOP隔离影响,即使窗口被隔离也能正常通信。
- 通过User-Agent判断Safari 15.x用户,临时将COOP设置为
关于unsafe-none的安全性
unsafe-none会关闭页面的跨源隔离保护,使页面暴露在跨源窗口劫持等风险中,确实不是长期合理方案,仅适合作为旧版Safari的临时兼容手段,优先推荐升级浏览器版本。
内容的提问来源于stack exchange,提问作者Jankapunkt
相关产品推荐
相关产品推荐

