第12章:WebRTC安全:DTLS-SRTP加密、证书管理、安全最佳实践
聊到WebRTC安全,我得先跟你掏心窝子说一句:很多人觉得WebRTC自带加密,所以随便用用就安全了——这是最大的误解。
我在前几年帮一家金融科技公司做远程开户系统时,就遇到过这么个事。对方CTO拍着胸脯说“WebRTC不是强制加密吗?我们肯定安全”。结果我一测,他们的信令服务器走的还是HTTP,证书是自签名的,而且居然允许任何客户端连接。嗯,这哪是加密,这分明是给攻击者开了个后门。
所以这一章,咱们就把WebRTC安全的底裤扒干净。从DTLS-SRTP怎么工作,到证书怎么管,再到实际项目中的安全最佳实践,我一个一个给你讲透。
12.1 为什么WebRTC必须加密?
你想想看,WebRTC传输的是啥?是实时音视频流。说白了,就是你和对方的对话、画面,甚至屏幕共享内容。如果这些数据在网络上裸奔,那跟在大街上喊银行卡密码有什么区别?
WebRTC规范强制要求所有媒体通道必须加密。这不是可选项,是硬性规定。浏览器如果检测到对方不支持加密,会直接拒绝连接。
核心要点:WebRTC使用两层加密——DTLS用于密钥协商和信令通道加密,SRTP用于媒体数据加密。两者配合,构成了WebRTC的安全基石。
12.2 DTLS-SRTP:WebRTC的加密双保险
DTLS-SRTP这个名字听起来挺唬人,其实拆开看就明白了:
- DTLS(Datagram Transport Layer Security):基于UDP的TLS。因为WebRTC用UDP传输,所以不能用传统的TCP+TLS。DTLS就是在UDP上实现了类似TLS的握手和加密。
- SRTP(Secure Real-time Transport Protocol):对RTP媒体流进行加密和认证。保证你听到的声音、看到的画面是完整的、没被篡改过的。
它们俩的关系是这样的:
- 首先,双方通过DTLS握手,协商出一套共享密钥。
- 然后,这套密钥被传递给SRTP,用于加密后续的媒体数据。
- DTLS本身也会继续存在,用于传输一些控制信息。
我个人习惯把DTLS比作“钥匙交换员”,SRTP比作“保险柜”。钥匙交换员先把钥匙安全地交给双方,然后双方用这把钥匙打开保险柜,往里面放数据。整个过程,外人既拿不到钥匙,也打不开保险柜。
小提示:DTLS-SRTP的握手过程通常发生在ICE连接建立之后。也就是说,先打通网络通道,再协商加密。这个顺序别搞反了。
12.3 证书管理:自签名还是CA签发?
说到证书,很多初学者会问:“WebRTC需要买SSL证书吗?”
答案是:不一定。
WebRTC在DTLS握手时使用的证书,可以是自签名的。因为DTLS-SRTP的证书验证机制和HTTPS不太一样。在WebRTC场景下,证书指纹(fingerprint)是通过信令通道交换的,只要信令通道是安全的,自签名证书就够用。
但是——这里有个大坑——如果信令通道本身不安全,自签名证书就毫无意义。
警告:千万不要在HTTP页面上使用WebRTC!浏览器会直接阻止getUserMedia()和RTCPeerConnection的调用。所有WebRTC应用必须运行在HTTPS环境下(或者localhost)。
我在项目中遇到过这样的情况:开发环境用localhost没问题,一上测试环境就报错。查了半天,发现测试环境用的是HTTP。嗯,这个坑我踩过一次就再也没忘过。
12.3.1 证书指纹交换
证书指纹是DTLS-SRTP安全的关键。流程是这样的:
- 每个Peer在创建RTCPeerConnection时,会生成一个自签名证书(或者使用你提供的证书)。
- 这个证书的SHA-256指纹会被计算出来。
- 指纹通过信令通道发送给对方。
- 对方在DTLS握手时,验证收到的证书是否和指纹匹配。
说白了,就是“先看照片再见面”。指纹就是照片,证书就是本人。如果照片对不上,说明中间有人冒充。
12.4 安全最佳实践:我踩过的坑和总结的经验
这部分是我最想跟你分享的。理论说再多,不如实战中摔一跤来得深刻。下面这些最佳实践,都是我拿真金白银换来的教训。
12.4.1 信令通道必须加密
这是最基础也最重要的一条。信令通道传输SDP和ICE候选信息,里面包含了证书指纹、IP地址、端口等敏感数据。如果信令通道是明文的,攻击者可以轻松篡改指纹,然后实施中间人攻击。
怎么做:使用WSS(WebSocket Secure)或者HTTPS作为信令传输协议。不要用WS或HTTP。
12.4.2 验证远程证书指纹
虽然WebRTC会自动验证DTLS证书,但有些场景下你需要手动验证。比如在P2P连接中,如果信令服务器被攻破,攻击者可以替换证书指纹。
我曾经帮一个客户排查问题,发现他们的应用在连接建立后,偶尔会出现“静默通话”的情况——双方都听不到声音。最后定位到是证书指纹验证没做,导致连接到了一个伪造的Peer上。
代码示例:手动验证指纹
// 假设你通过安全信令通道收到了对方的指纹
const expectedFingerprint = "SHA-256 00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF";
peerConnection.addEventListener('connectionstatechange', event => {
if (peerConnection.connectionState === 'connected') {
// 获取远程证书
const stats = await peerConnection.getStats();
stats.forEach(report => {
if (report.type === 'transport' && report.dtlsState === 'connected') {
const remoteCert = report.selectedCandidatePair?.remoteCandidate?.certificateId;
// 这里需要进一步获取证书指纹并比对
// 实际代码会更复杂,这里只展示思路
console.log('DTLS连接已建立,建议验证指纹');
}
});
}
});
经验之谈:在实际项目中,我建议在应用层也做一次指纹比对。虽然WebRTC底层已经做了,但多一层验证多一层保障。尤其是金融、医疗等对安全性要求极高的场景。
12.4.3 证书轮换
自签名证书虽然方便,但也不是一劳永逸的。如果证书私钥泄露,攻击者可以冒充你的身份。
最佳做法:定期轮换证书。在WebRTC中,你可以通过RTCCertificate API来控制证书的生命周期。
// 生成一个有效期为24小时的证书
const cert = await RTCPeerConnection.generateCertificate({
name: 'ECDSA',
namedCurve: 'P-256',
expires: Date.now() + 24 * 60 * 60 * 1000 // 24小时后过期
});
const pc = new RTCPeerConnection({
certificates: [cert]
});
嗯,这里要注意:expires参数不是所有浏览器都支持。Chrome支持,但Safari可能忽略。所以不要完全依赖这个机制,最好在应用层也做证书管理。
12.4.4 禁用不安全的加密套件
WebRTC默认会协商使用最强的加密套件,但有些老旧客户端可能会尝试降级。我建议在服务端(如果是SFU/MCU架构)强制指定加密套件。
| 加密套件 | 安全性 | 建议 |
|---|---|---|
| TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 | 高 | 推荐使用 |
| TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 | 高 | 推荐使用 |
| TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA | 中 | 尽量避免 |
| TLS_RSA_WITH_AES_128_CBC_SHA | 低 | 禁用 |
12.4.5 媒体流访问控制
安全不只是加密,还包括权限控制。WebRTC的getUserMedia()会弹出权限请求框,但有些用户会不小心点“允许”。
我曾经见过一个案例:某个在线教育平台,老师端在共享屏幕时,不小心把包含学生隐私信息的窗口也共享出去了。这不是技术漏洞,是使用习惯问题。但作为开发者,我们可以从技术上做一些限制。
建议:
- 只在需要时才请求媒体权限,不要一打开页面就请求。
- 提供清晰的UI提示,告诉用户当前哪些设备正在被使用。
- 支持用户随时停止共享或关闭摄像头/麦克风。
12.5 知识体系总览
下面这张图,我把本章的核心知识点串起来了。你可以把它当作一个快速参考地图。
12.6 写在最后
WebRTC的安全,说白了就是“信任链”的问题。从信令通道到DTLS握手,从证书指纹到媒体加密,每一个环节都是在建立和验证信任。任何一个环节出问题,整个安全体系就形同虚设。
我个人觉得,做实时通信最怕的就是“想当然”。觉得浏览器帮我做了加密,我就什么都不用管了。其实不然。浏览器只负责传输层的安全,应用层的安全——比如用户身份认证、信令通道保护、证书管理——这些都得你自己来。
好了,这一章的内容就到这里。记住我上面说的那些坑,你在实际项目中能少走很多弯路。