DTLS
History
稳定性:1 - 实验性
node:dtls 模块提供了在 UDP 之上实现的数据报传输层安全(DTLS)协议。DTLS 为基于数据报的通信提供与 TLS 等价的安全保障,包括机密性、完整性和身份认证。
要使用此模块,必须在构建时通过 --experimental-dtls 配置标志启用,并在运行时通过 --experimental-dtls CLI 标志启用。
node --experimental-dtls app.mjs
import { listen, connect } from 'node:dtls';
const { listen, connect } = require('node:dtls');
使用 [权限模型][] 时,必须传入 --allow-net 标志以允许 DTLS 网络操作。
如果没有它,调用 dtls.connect() 或 dtls.listen() 将抛出
ERR_ACCESS_DENIED 错误。
node --permission --allow-fs-read=* --experimental-dtls index.mjs Error: 对此 API 的访问已受限。请使用 --allow-net 来管理权限。 code: 'ERR_ACCESS_DENIED', permission: 'Net', }
即使没有 --allow-net,也允许创建一个 DTLSEndpoint 实例而不进行连接或监听,
因为在调用 dtls.connect() 或 dtls.listen() 之前不会发生网络 I/O。
DTLS 专为 UDP 传输而设计,并在以下几个关键方面与 TLS 不同:
- 不保证流式传输:消息可能乱序到达或丢失。 DTLS 保留数据报语义。
- 一个套接字,多个对端:单个 UDP 套接字可服务多个 DTLS
会话。
DTLSEndpoint负责管理这种多路复用。 - Cookie 交换:DTLS 服务器使用无状态 cookie 机制 (HelloVerifyRequest)来防止拒绝服务放大攻击。
- 重传:由于 UDP 不保证送达,DTLS 会在内部处理握手重传。
dtls.listen(callback, options): void
创建一个绑定到指定地址和端口的 DTLS 服务器。该服务器 使用基于 HMAC 的自动 cookie 交换进行 DoS 防护。
import { listen } from 'node:dtls'; import { readFileSync } from 'node:fs'; const endpoint = listen((session) => { session.onmessage = (data) => { console.log('收到:', data.toString()); session.send('pong'); }; session.onhandshake = (protocol) => { console.log('握手完成:', protocol); }; }, { cert: readFileSync('server-cert.pem'), key: readFileSync('server-key.pem'), port: 4433, }); console.log('DTLS 服务器正在监听', endpoint.address);
dtls.connect(host, port, options?): void
stringnumberObjectbooleantrue
时,服务器证书必须同时链接到受信任的 CA,并且与预期的身份(
servername
,或者未设置
servername
时的
host
)匹配;否则握手将中止,且
session.opened
将拒绝。为
false
时,不验证证书。
默认值:
true
。stringhost
参数。设置为
''
可禁用 SNI。对于 IP 地址字面量,永远不会发送 SNI。string'0.0.0.0'
。number0
(临时端口)。stringnumber1200
。连接到 DTLS 服务器。返回一个 DTLSSession,其 opened 属性是一个
Promise,在握手完成时解析。
import { connect } from 'node:dtls'; import { readFileSync } from 'node:fs'; const session = connect('localhost', 4433, { ca: [readFileSync('ca-cert.pem')], }); await session.opened; session.send('hello'); session.onmessage = (data) => { console.log('收到:', data.toString()); };
类:DTLSEndpoint
History
管理一个 UDP 套接字并对 DTLS 会话进行多路复用。
- 返回:
Object{ address, family, port }
该端点绑定到的本地地址。
- 返回:
DTLSEndpointState
包含以下属性的共享状态对象:
为此端点收集的统计信息。只读。该统计对象是 实时的,并随着数据流经端点由 C++ 内部更新。
当为 true 时,端点会拒绝新的传入连接。可用于实现背压。
endpoint.close(): void
- 返回:
Promise在端点完全关闭时解析。
优雅地关闭端点。在释放 UDP 套接字之前,所有活动会话都会通过
close_notify 警报关闭。
endpoint.destroy(error?): void
立即销毁端点,不发送 close_notify 警报。
endpoint[Symbol.asyncDispose](): void
等同于调用 endpoint.close()。
类:DTLSEndpoint.Stats
History
端点收集到的统计信息视图。
- 类型:
bigint指示端点创建时间的时间戳。只读。
- 类型:
bigint指示端点销毁时间的时间戳。只读。
- 类型:
bigint此端点接收的字节总数。只读。
- 类型:
bigint此端点发送的字节总数。只读。
- 类型:
bigint此端点接收的 UDP 数据包总数。只读。
- 类型:
bigint此端点发送的 UDP 数据包总数。只读。
- 类型:
bigint此端点接受的由对端发起的会话总数。只读。
- 类型:
bigint由此端点发起的会话总数。只读。
- 类型:
bigint因端点被标记为忙碌而被拒绝的传入连接总数。只读。
- 类型:
boolean
如果统计对象仍连接到底层端点,则为 true。
一旦端点被销毁,统计信息就会变为过时快照。
类:DTLSSession
History
表示与单个远程对端的 DTLS 关联。
session.send(data): void
向对端发送应用数据。数据在通过 UDP 发送之前会由 DTLS 加密。
只能在握手完成后调用(session.opened 已解析)。
session.close(): void
- 返回:
Promise在会话关闭时解析。
通过发送 close_notify 警报来启动优雅的 DTLS 关闭。
session.destroy(error?): void
立即销毁会话,不发送 close_notify。
{ protocol }
。- 返回:
Object{ address, family, port }
- 返回:
string协商得到的 DTLS 协议版本 (例如,'DTLSv1.2')。
- 返回:
Object{ name, standardName, version }
为此会话收集的统计信息。只读。该统计对象是 实时的,并随着数据流经会话而更新。
session.exportKeyingMaterial(length, label, context?): void
从 DTLS 会话导出密钥材料,如 RFC 5705 所定义。 这通常与 DTLS-SRTP 一起使用,以为媒体流派生加密密钥。
类:DTLSSession.Stats
History
会话收集到的统计信息视图。
- 类型:
bigint指示会话创建时间的时间戳。只读。
- 类型:
bigint指示会话销毁时间的时间戳。只读。
- 类型:
bigint指示调用close()时间的时间戳。只读。
- 类型:
bigint指示 DTLS 握手完成时间的时间戳。只读。
- 类型:
bigint接收到的应用数据字节总数。只读。
- 类型:
bigint发送的应用数据字节总数。只读。
- 类型:
bigint接收到的应用消息总数。只读。
- 类型:
bigint发送的应用消息总数。只读。
- 类型:
bigintDTLS 握手重传总次数。只读。
- 类型:
boolean
如果统计对象仍连接到底层会话,则为 true。
一旦会话被销毁,统计信息就会变为过时快照。
设置后可接收来自对端的应用数据。
设置后可接收错误通知。
设置后可接收握手完成通知。
设置后可接收 TLS 密钥日志行(用于使用 Wireshark 调试)。
session[Symbol.asyncDispose](): void
等同于调用 session.close()。
DTLS-SRTP 被 WebRTC 用于媒体加密。DTLS 握手会协商 SRTP 保护配置文件并提供密钥材料。
import { listen, connect } from 'node:dtls'; import { readFileSync } from 'node:fs'; // 带 SRTP 的服务器 const server = listen((session) => { session.onhandshake = () => { console.log('SRTP 配置文件:', session.srtpProfile); const keys = session.exportKeyingMaterial( 60, 'EXTRACTOR-dtls_srtp', ); console.log('SRTP 密钥材料:', keys); }; }, { cert: readFileSync('server-cert.pem'), key: readFileSync('server-key.pem'), port: 5004, srtp: 'SRTP_AES128_CM_SHA1_80:SRTP_AEAD_AES_128_GCM', }); // 带 SRTP 的客户端 const session = connect('localhost', 5004, { rejectUnauthorized: false, srtp: 'SRTP_AEAD_AES_128_GCM:SRTP_AES128_CM_SHA1_80', }); await session.opened; console.log('协商的 SRTP:', session.srtpProfile); const keys = session.exportKeyingMaterial(60, 'EXTRACTOR-dtls_srtp');
由于 libuv 当前不支持路径 MTU 发现,DTLS 模块使用保守的默认 MTU 1200 字节。这个值适用于几乎所有网络路径,但在本地网络中可能并非最优。
可以通过 mtu 选项配置 MTU:
// 用于你已知路径 MTU 的本地网络 const endpoint = listen(callback, { // ... mtu: 1400, });
允许的最小 MTU 为 256 字节。最大值为 65535。