错误信息
TypeError: Cannot read properties of undefined (reading 'digest')
也可能输出为
- TypeError: Cannot read properties of undefined (reading 'importKey')
- TypeError: can't access property "digest", crypto.subtle is undefined
- TypeError: undefined is not an object (evaluating 'crypto.subtle.digest')
这段文字是引擎通用的 TypeError:Chromium/V8 为“Cannot read properties of undefined (reading 'digest')”,Firefox 为“can't access property \"digest\", crypto.subtle is undefined”,Safari/WebKit 为“undefined is not an object (evaluating 'crypto.subtle.digest')”。其中的方法名取决于第一次调用的方法(`importKey`、`sign`、`generateKey` 等)。
WebCrypto — window.crypto.subtle is undefined outside a secure context (Chromium / Firefox / WebKit)
含义
这个页面上不存在 `crypto.subtle`,因此对它的第一次调用就会报出一个笼统的“undefined”TypeError。浏览器只在安全上下文中提供 `SubtleCrypto`:通过 HTTPS 提供的页面、来自 `http://localhost` / `127.0.0.1` 的页面,或者 `file:` URL。同样的代码在你自己机器上的 localhost 中能正常运行,但只要通过普通 HTTP、用另一个主机名或 IP 地址打开页面,就会出错。`crypto.getRandomValues` 仍然可用,所以这个故障看起来像是只影响部分功能。
原因
页面通过普通 HTTP 提供
常见没有启用 TLS 的预发布主机、内网工具和预览环境都不是安全上下文。按照规范,所有 WebCrypto 功能在这些地方都不可用。
通过局域网地址打开开发服务器
常见`http://localhost:5173` 是安全的,`http://192.168.1.20:5173`(在手机上测试时)则不是。开发时能正常运行的代码,到了设备上就会失败。
不安全的框架或 WebView
偶见嵌入在 `http:` 父框架中的安全页面,或者通过自定义的不安全协议加载本地内容的 WebView,同样不是安全上下文。
修复方法
- 1.
检测这种情况,并给出明确的错误提示
在使用 WebCrypto 之前检查 `window.isSecureContext`。不要悄悄回退到用 JavaScript 实现的密码学 polyfill——没有 TLS,页面本身的完整性本来就无法保证。
js if (!window.isSecureContext || !globalThis.crypto?.subtle) { throw new Error("WebCrypto needs HTTPS (or http://localhost). Current origin: " + location.origin); } const digest = await crypto.subtle.digest("SHA-256", data); - 2.
通过 HTTPS 提供页面
在每个环境中都使用 TLS,包括你从其他设备访问的开发服务器;mkcert 等工具可以创建本地受信任的证书。
bash # Local HTTPS for a dev server reachable from other devices mkcert -install mkcert dev.example.test 192.168.1.20 localhost # Vite: server.https = { key: fs.readFileSync("dev.example.test+2-key.pem"), cert: fs.readFileSync("dev.example.test+2.pem") }
相关错误
DataError: Data provided to an operation does not meet requirementsWebCrypto crypto.subtle.importKeyTypeError: Failed to execute 'importKey' on 'SubtleCrypto': The provided value…WebCrypto SubtleCrypto.importKeySecurityError: The relying party ID is not a registrable domain suffix of, nor…WebAuthn navigator.credentials.create() / get()