webrtc data channels
on this page
webrtc data channels provide peer-to-peer network tunneling using the webrtc datachannel api with dtls encryption and nat traversal capabilities.
technical description
webrtc (web real-time communication) uses dtls over udp for encryption with configurable ordered/unordered delivery. data channels provide a browser-based api for arbitrary data exchange between peers without server intermediaries.
the technique works by:
- ice protocol for nat traversal using stun/turn servers
- dtls handshake for encrypted p2p connection
- sctp-over-dtls for reliable data delivery
- custom application-level tunneling protocol
implementation: rtctunnel
overview
rtctunnel (https://github.com/rtctunnel/rtctunnel) implements p2p network tunnels using webrtc datachannels with:
- peer identification via public keys
- automatic nat traversal
- encrypted data transmission
- cross-platform support (go implementation)
- performance: 90-95% of udp throughput
installation
# from binary releases
wget https://github.com/rtctunnel/rtctunnel/releases/latest/download/rtctunnel-linux
chmod +x rtctunnel-linux
# from source
git clone https://github.com/rtctunnel/rtctunnel.git
cd rtctunnel
go build -o rtctunnel ./cmd/rtctunnel
server setup
# generate server keypair
rtctunnel genkey -o server.key
# extract public key
rtctunnel pubkey -k server.key > server.pub
# start server
rtctunnel server -k server.key -p 8080
client connection
# connect to server using public key
rtctunnel client -s wss://server.example.com:8080 \
-k server.pub \
-l 127.0.0.1:8022 \
-r 127.0.0.1:22
# ssh through tunnel
ssh -p 8022 user@127.0.0.1
configuration
# rtctunnel.yaml
server:
key_file: 'server.key'
bind_addr: ':8080'
ice_servers:
- urls: ['stun:stun.l.google.com:19302']
client:
server_url: 'wss://server.example.com:8080'
server_pubkey_file: 'server.pub'
local_addr: '127.0.0.1:8022'
remote_addr: '127.0.0.1:22'
implementation: tor snowflake
overview
tor snowflake (https://github.com/keroserene/snowflake) routes traffic through webrtc to circumvent censorship:
- makes traffic appear as voice/video calls
- volunteer proxy network
- browser-based proxy support
- automatic failover mechanisms
proxy setup
// browser-based proxy
const snowflake = new Snowflake({
brokerUrl: 'https://snowflake-broker.torproject.net/',
relayAddr: {
host: '127.0.0.1',
port: 9902,
},
});
snowflake.setRelayAddr('127.0.0.1', 9902);
snowflake.beginWebRTC();
tor configuration
# torrc configuration
clienttransportplugin snowflake exec ./client \
-url https://snowflake-broker.torproject.net/ \
-front cdn.sstatic.net \
-ice stun:stun.l.google.com:19302
standalone proxy
# compile snowflake proxy
git clone https://git.torproject.org/pluggable-transports/snowflake.git
cd snowflake/proxy
go build
# run proxy
./proxy -broker https://snowflake-broker.torproject.net/ \
-relay wss://snowflake.torproject.net/ \
-stun stun:stun.l.google.com:19302
implementation: torkameleon
overview
torkameleon (https://github.com/afonsovilalonga/torkameleon) provides:
- webrtc mimicry for tor traffic
- multiple transport protocols
- traffic shaping capabilities
- protocol obfuscation
configuration
{
"transport": "webrtc",
"ice_servers": [{ "urls": "stun:stun.l.google.com:19302" }],
"signaling_server": "wss://signal.example.com",
"obfuscation": true,
"traffic_shaping": {
"enabled": true,
"pattern": "video_call"
}
}
traffic characteristics
webrtc signaling
client -> signaling server: offer (sdp)
signaling server -> peer: offer
peer -> signaling server: answer (sdp)
signaling server -> client: answer
ice candidate exchange:
candidate:foundation priority type address port
data channel traffic
# packet analysis with scapy
from scapy.all import *
def analyze_webrtc_traffic(pcap_file):
packets = rdpcap(pcap_file)
# identify dtls handshakes
dtls_packets = []
for p in packets:
if p.haslayer(UDP):
payload = bytes(p[UDP].payload)
# dtls content types: 20=change_cipher, 21=alert, 22=handshake, 23=application
if len(payload) > 0 and payload[0] in [20, 21, 22, 23]:
dtls_packets.append(p)
print(f"found {len(dtls_packets)} potential dtls packets")
# analyze packet timing and sizes
sizes = [len(p) for p in dtls_packets]
print(f"avg packet size: {sum(sizes)/len(sizes):.1f} bytes")
analyze_webrtc_traffic('webrtc_tunnel.pcap')
ice candidate leakage
// detect webrtc ip leakage
function getWebRTCIPs() {
const ips = [];
const rtc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
});
rtc.createDataChannel('');
rtc.onicecandidate = (e) => {
if (e.candidate) {
const ip = e.candidate.candidate.match(/(\d+\.\d+\.\d+\.\d+)/);
if (ip) ips.push(ip[1]);
}
};
rtc.createOffer().then(rtc.setLocalDescription.bind(rtc));
return ips;
}
detection methods
traffic indicators
- unusual webrtc connection patterns outside media contexts
- high data volumes over data channels vs media streams
- ice candidate patterns suggesting automation
- dtls certificate anomalies
- persistent connections with low media traffic
detection rules
# suricata rule for webrtc tunneling
alert tls any any -> any any (
msg:"suspicious webrtc data channel usage";
tls.sni; content:"webrtc"; nocase;
flow:established,to_server;
threshold: type limit, track by_src, seconds 3600, count 100;
sid:1000010;
)
behavioral analysis
# webrtc connection analysis
import json
from collections import defaultdict
def analyze_webrtc_connections(flows):
webrtc_flows = []
for flow in flows:
# identify webrtc by port ranges and dtls
if (flow['dst_port'] in range(10000, 65535) and
flow['protocol'] == 'UDP' and
'dtls' in flow.get('app_proto', '')):
webrtc_flows.append(flow)
# group by source ip
src_connections = defaultdict(list)
for flow in webrtc_flows:
src_connections[flow['src_ip']].append(flow)
# flag suspicious patterns
for src_ip, flows in src_connections.items():
if len(flows) > 10: # many connections
data_ratio = sum(f.get('bytes', 0) for f in flows) / len(flows)
if data_ratio > 10000: # high data per connection
print(f"suspicious webrtc usage from {src_ip}")
# example usage with network flow data
flows = [
{'src_ip': '192.168.1.10', 'dst_port': 45678, 'protocol': 'UDP',
'app_proto': 'dtls', 'bytes': 150000},
# ... more flows
]
analyze_webrtc_connections(flows)
countermeasures
network level
# block webrtc ports (dynamic range)
iptables -A FORWARD -p udp --dport 10000:65535 -m recent --update --seconds 10 --hitcount 50 -j DROP
# restrict stun/turn servers
iptables -A FORWARD -p udp --dport 3478 -j DROP # standard stun
iptables -A FORWARD -p tcp --dport 3478 -j DROP # stun over tcp
iptables -A FORWARD -p udp --dport 5349 -j DROP # stuns (secure)
application level
// disable webrtc in browser policies
{
"WebRtcUdpPortRange": "",
"WebRtcLocalIpsAllowedUrls": [],
"DefaultWebRtcUdpPortRange": ""
}
monitoring
# monitor webrtc connections
ss -tuln | grep -E ":(10000|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])"
# analyze dtls certificates
tshark -r webrtc_traffic.pcap -Y "dtls.handshake.type == 11" \
-T fields -e dtls.handshake.certificate
advantages and limitations
advantages
- true peer-to-peer communication
- strong encryption (dtls)
- nat traversal capabilities
- browser-based deployment
- appears as legitimate media traffic
- difficult to block without breaking voip/video
limitations
- requires signaling server infrastructure
- ice negotiation can reveal ip addresses
- limited by webrtc browser policies
- stun/turn servers may be blocked
- higher overhead than raw protocols
performance metrics
bandwidth comparison
implementation | throughput | overhead | latency |
---|---|---|---|
rtctunnel | 90-95% udp | ~5-10% | +2-5ms |
tor snowflake | 50-70% tcp | ~30-50% | +50-100ms |
native webrtc | 85-95% udp | ~5-15% | +1-3ms |
connection establishment
# measure webrtc connection time
start_time=$(date +%s%3N)
# ... webrtc connection code ...
end_time=$(date +%s%3N)
echo "connection time: $((end_time - start_time))ms"
# typical times:
# ice gathering: 1-3 seconds
# dtls handshake: 200-500ms
# data channel open: 100-200ms
browser integration
chrome extension
// manifest.json
{
"permissions": ["webRequest", "webRequestBlocking", "*://*/*"],
"background": {
"scripts": ["background.js"]
}
}
// background.js - webrtc tunnel proxy
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
if (details.url.includes('target-domain.com')) {
// redirect through webrtc tunnel
return {redirectUrl: 'http://localhost:8080' + details.url.pathname};
}
},
{urls: ["<all_urls>"]},
["blocking"]
);
firefox addon
// background script
function setupWebRTCTunnel() {
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
});
const dc = pc.createDataChannel('tunnel', {
ordered: true,
});
dc.onopen = () => {
console.log('webrtc tunnel established');
// proxy browser requests through data channel
};
return pc;
}
real-world usage
censorship circumvention
- tor snowflake: 10,000+ daily users
- webrtc proxies: widespread in restrictive countries
- voice-over-ip mimicry: blends with legitimate traffic
enterprise environments
- rtctunnel: remote access through firewalls
- peer-to-peer file sharing: direct transfers
- collaborative tools: whiteboard/document sharing
testing setup
local development
# setup test environment
git clone https://github.com/rtctunnel/rtctunnel.git
cd rtctunnel
# generate test keys
./rtctunnel genkey -o test.key
./rtctunnel pubkey -k test.key > test.pub
# start local server
./rtctunnel server -k test.key -p 8080 &
# test client connection
./rtctunnel client -s ws://localhost:8080 -k test.pub \
-l 127.0.0.1:2222 -r 127.0.0.1:22
# verify tunnel
ssh -p 2222 localhost
performance testing
# bandwidth test through webrtc tunnel
iperf3 -s &
iperf3 -c 127.0.0.1 -p 2222 -t 60
# latency measurement
ping -c 100 -i 0.1 127.0.0.1 | grep avg
references
- rfc 8831: webrtc data channels
- rfc 8445: interactive connectivity establishment (ice)
- rfc 6347: datagram transport layer security (dtls)
- “webrtc security analysis” - ietf security considerations
- tor project snowflake documentation