Skip to content

安全上下文与混合内容

基于 Web 现代标准 · 核于 2026-07

速查

  • 安全上下文(secure context):满足最低认证与保密标准的 Window/Worker——目标是不让中间人(MITM)碰到强能力 API;判定结果挂在 window.isSecureContext
  • 算安全的:https:///wss://,以及「本机投递」的 http://127.0.0.1http://localhosthttp://*.localhost(Firefox 84+ 才认 localhost)和 file://——这就是「本地开发能用 SW,一上 http 测试机就不行」的原因
  • 这类不走 HTTPS 也可信的源叫 potentially trustworthy origin(潜在可信源)
  • iframe 规则:整条祖先链必须全部安全——HTTPS iframe 嵌在 HTTP 页面里不算安全上下文;反之,弹窗不看 opener:不安全页面开的新窗口按新窗口自己的顶层判定
  • 强能力 API 全面门控:Service Worker、getUserMedia、Geolocation、异步 Clipboard、Web Crypto、WebUSB/HID/Serial/Bluetooth、Push/Notifications、WebAuthn、Payment Request、WebGPU、WebTransport、Screen Capture……不在安全上下文里直接不存在或直接拒绝
  • 混合内容(mixed content):HTTPS 页面加载 http: 子资源——密文页面上的明文窟窿,中间人可窥可改
  • 现行规范分两类处置:可升级(upgradable)——<img src><audio src><video src>、CSS 背景图等自动改写为 https 重试可阻断(blockable)——<script>、样式表、<iframe>fetch/XHR、字体、srcset/<picture>直接拦截
  • 这是 2020 年前后的换代:旧模型「主动内容拦、被动内容仅警告」已退场——别再背「图片只是警告」,现在是自动升级,升不上去就是坏
  • IP 直连例外http://93.184.215.14/img.png 这类 IP 主机不升级、直接阻断(域名才升级)——内网设备场景的豁免走 Local Network Access
  • upgrade-insecure-requests CSP 指令:把页面所有 http 子请求(含可阻断类)改写为 https——存量 http 链接多的老站迁移利器;block-all-mixed-content 已废弃,别再配
  • TLS 本身(握手/证书/HSTS)见网络章 · HTTPS 与 TLS,本页只管「浏览器据此开/关什么」
  • 排查:DevTools Console 对升级与阻断各有一条警告;升级后的请求在 Network 面板 URL 已是 https

一、安全上下文:强能力的准入门槛

浏览器把 API 分了两档。DOM 操作、fetch、CSS 动画这类「常规能力」哪都能用;而强能力(powerful features)——读你的摄像头、拿你的地理位置、注册常驻的 Service Worker、直连 USB 设备——被圈进安全上下文才开放。

动机直指中间人:HTTP 页面的每一个字节都可能被网络路径上的任何一跳篡改。如果强能力在 HTTP 下可用,「攻破咖啡馆 Wi-Fi」就等于「往任意页面注入一段调 getUserMedia 的脚本」。MDN 的表述——安全上下文的主要目标是防止 MITM 攻击者接触到能进一步加害用户的强能力 API

1.1 判定规则

一个上下文是否安全,看它的投递通道顶层环境

上下文安全?说明
https:// / wss://正道
http://127.0.0.1 / http://localhost / http://*.localhost本机投递,不经网络——potentially trustworthy origin(Firefox 84+ 才认 localhost 域名写法)
file://本地文件
http:// 其他一切包括内网 IP:http://192.168.1.10 不是安全上下文
HTTPS iframe ⊂ HTTP 页面祖先链一票否决:所有祖先都必须安全
HTTP 页面 window.open 出的 HTTPS 窗口弹窗按自己的顶层判定,不看 opener 是否安全
js
// 特性检测:别猜,问浏览器
if (window.isSecureContext) {
  // 安全上下文:SW 可注册
  navigator.serviceWorker.register("/sw.js");
} else {
  console.warn("非安全上下文:强能力 API 不可用");
}
// Worker 里同样有 self.isSecureContext

两个高频踩坑:局域网联调——手机访问 http://192.168.1.10:5173 时 SW/摄像头全灭(本机 localhost 好好的),解法是 devServer 开 HTTPS(自签/mkcert)或用支持 HTTPS 的隧道;iframe 集成——你的 HTTPS 应用被客户嵌进 HTTP 老门户,强能力集体失效,这是祖先链规则,不是你的 bug。

1.2 强能力门控清单

MDN 维护完整列表(几十项),前端高频的按用途归组:

用途被门控的 API
离线与消息Service Workers、Push API、Notifications API、Background Sync/Fetch
媒体采集MediaDevices.getUserMedia()(摄像头/麦克风)、Screen Capture、Audio Output Devices
定位与传感Geolocation API、Generic Sensor(加速度计/陀螺仪)、设备方向/运动
剪贴板与文件异步 Clipboard API、File System API
密码学与凭证Web Crypto APIcrypto.subtle)、Web Authentication(WebAuthn)、Credential Management、WebOTP
硬件直连WebUSB、Web Bluetooth、WebHID、Web Serial、Web NFC、Web MIDI
支付与系统Payment Request、Web Share、Storage API(持久化)、Screen Wake Lock、Idle Detection
新锐图形/传输WebGPU、WebCodecs、WebTransport、WebXR

记忆模型:「这个能力落到中间人手里可怕吗?」可怕就在清单上。失败形态不统一——有的 API 在非安全上下文里干脆 undefined(如 navigator.serviceWorker),有的返回拒绝的 Promise,特性检测时两种都要兜。

二、混合内容:密文页面上的明文窟窿

页面本身走了 HTTPS,里面却挂着 http:// 的子资源——这就是混合内容。它把 HTTPS 的承诺撕开一道口子:明文资源在路上可被窥探(泄露用户在看什么),更糟的是可被替换——尤其当那是脚本或样式时,等于把页面控制权交给中间人。

2.1 现行分类:可升级 vs 可阻断

现行 Mixed Content 规范按「拦掉会不会大面积坏站」把 http 子资源分成两类处置:

类别覆盖的资源浏览器行为
可升级(upgradable)<img src>(含 SVG,但不含 srcset)、CSS 图像类 url()background-image/border-image/cursor 等,皆为 image destination)、<audio src><video src><source>自动把 URL 改写为 https 再请求;服务器若不支持 https,加载失败
可阻断(blockable)<script><link> 样式表、<iframe>fetch()/XHR、非图像类的 CSS url()(如 @font-face Web 字体)、<object data>sendBeacon()<img srcset>/<picture>……以及一切新类型直接阻断,不发请求

这是一次换代,别沿用旧笔记:早期规范分「主动(active)/被动(passive)」——脚本这类能改页面的必拦,图片这类「只展示」的允许加载、仅在控制台警告加小图标。新规范把旧「被动/可选阻断」清单平移成了「可升级」:不再放行明文,而是替你升到 https;所有新增资源类型一律归入可阻断。所以 2020 年后的正确心智是:HTTPS 页面里不存在「还能明文加载」的子资源——要么被升级、要么被拦

两个边界:

  • IP 主机不升级、直接拦<img src="http://example.com/a.png"> 会升级,<img src="http://93.184.215.14/a.png"> 直接阻断——IP 直连大概率是内网设备,盲升 https 只会换一种失败。这条路的正解是 Local Network Access 的权限化豁免(权限授予后对本地目标放宽混合内容检查)。
  • 混合下载(mixed downloads):HTTPS 页面里指向 http 的下载同样被现代浏览器视为风险处置。

2.2 upgrade-insecure-requests 与已废弃的 block-all-mixed-content

存量老站数据库里躺着上万条 http:// 图片外链时,逐条改写不现实。CSP 提供一键升级:

http
# 页面所有 http 子请求(包括可阻断类:脚本、iframe、fetch……)在发出前统一改写为 https
Content-Security-Policy: upgrade-insecure-requests

它比浏览器默认行为更进一步——默认只升「可升级类」,这个指令连可阻断类一起升。注意它改写的是本页面的子请求,不负责别人链到你的 http 入口(那是 HSTS 的活,见网络章 · HTTPS 与 TLS)。

反方向的 block-all-mixed-content(连可升级类也一律拦)已废弃:升级语义成为默认后它失去意义,MDN 明确建议不再使用——存量配置见到可以删。

2.3 DevTools 里怎么看

  • Console:升级类打印「Mixed Content: … was loaded over HTTPS, but requested an insecure … This request has been upgraded」;阻断类打印「…This request has been blocked; the content must be served over HTTPS」。
  • Network:升级成功的资源 URL 列已是 https://;被拦的请求标 (blocked:mixed-content)
  • 地址栏锁形图标变化/「不安全」提示也是线索;上线前可用爬虫类工具全站扫 http:// 引用。

2.4 HTTPS 化上线自查清单

老站切 HTTPS 时按序过一遍,能把「切完一片坏」压到最小:

  1. 全站扫库存:模板、数据库富文本、CSS 里的 http:// 引用(重点:@font-facebackground-image、老编辑器插入的图片)。
  2. 能改的改协议相对以外的显式 https://;数量太大改不动的,上 upgrade-insecure-requests 兜底。
  3. 确认第三方资源都支持 HTTPS——可升级类升不上去等于坏图,可阻断类直接功能缺失。
  4. IP 直连的内网/设备请求单独处理:走 LNA 权限 而不是指望升级。
  5. 用 Report-Only 型 CSP 收集残余违规(img-src/connect-src 报告里混着 http 的就是漏网之鱼),再收口。
  6. 最后配 HSTS 锁死回退路径(见网络章)。

小结

安全上下文回答「强能力凭什么开放」:https://、本机投递(127.0.0.1/localhost/file://,即 potentially trustworthy origin)才算数,iframe 要整条祖先链安全、弹窗只看自己顶层,代码里以 isSecureContext 为准;SW、getUserMedia、Clipboard、Web Crypto、WebUSB、WebGPU 等几十项强能力全在门控清单上。混合内容回答「密文页面里的明文资源怎么办」:现行规范二分处置——img/audio/video 等自动升级,script/iframe/fetch 等直接阻断,IP 主机例外(拦而不升);旧的「被动内容仅警告」心智作废。迁移利器是 CSP upgrade-insecure-requests(连可阻断类一起升),而 block-all-mixed-content 已废弃。安全上下文同时也是下一页两大机制的准入前提——能力策略与内网访问权限:能力与元数据防护