DTLS
History
稳定性:1 - 实验性
node:dtls 模块提供了在 UDP 之上实现的 Datagram Transport
Layer Security(DTLS)协议。DTLS 为基于数据报的通信提供与 TLS 等价的
安全保障,包括机密性、完整性和身份认证。
要使用此模块,必须在构建时通过 --experimental-dtls 配置标志启用,
并在运行时通过 --experimental-dtls CLI 标志启用。
node --experimental-dtls app.mjs使用 [权限模型][] 时,必须传入 --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<Object><string><string><number><string>'0.0.0.0'
。<string>
|
<string[]><string><string[]><string>'SRTP_AES128_CM_SHA1_80:SRTP_AEAD_AES_128_GCM'
)。<boolean>false
。<number>1200
。创建一个绑定到指定地址和端口的 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 server listening on', endpoint.address);dtls.connect(host, port, options?): void<string><number><Object><string>
|
<string[]><string><string><boolean>true
。<string>'0.0.0.0'
。<number>0
(临时端口)。<string[]><string><number>1200
。连接到 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 }
- 返回:
<string>|<undefined>对端的 PEM 格式证书。
- 返回:
<string>|<undefined>协商得到的 ALPN 协议。
- 返回:
<string>|<undefined>协商得到的 SRTP 保护配置文件名称。
为此会话收集的统计信息。只读。该统计对象是 实时的,并随着数据流经会话而更新。
session.exportKeyingMaterial(length, label, context?): void从 DTLS 会话导出密钥材料,如 RFC 5705 所定义。 这通常与 DTLS-SRTP 一起使用,以为媒体流派生加密密钥。
类:DTLSSession.Stats
History
会话收集到的统计信息视图。
- 类型:
<bigint>指示会话创建时间的时间戳。只读。
- 类型:
<bigint>指示会话销毁时间的时间戳。只读。
- 类型:
<bigint>指示调用close()时间的时间戳。只读。
- 类型:
<bigint>指示 DTLS 握手完成时间的时间戳。只读。
- 类型:
<bigint>接收到的应用数据字节总数。只读。
- 类型:
<bigint>发送的应用数据字节总数。只读。
- 类型:
<bigint>接收到的应用消息总数。只读。
- 类型:
<bigint>发送的应用消息总数。只读。
- 类型:
<bigint>DTLS 握手重传总次数。只读。
- 类型:
<boolean>
如果统计对象仍连接到底层会话,则为 true。
一旦会话被销毁,统计信息就会变为过时快照。
设置后可接收来自对端的应用数据。
<Error>设置后可接收错误通知。
<string>设置后可接收握手完成通知。
<string>设置后可接收 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。