WebRTC mDNS Candidates and Local-Network Privacy
Understand WebRTC mDNS host candidates, RFC 8828 privacy goals, and bounded browser evidence.
BotBrowser Team
Want the structured docs for Network?
This article lives in the editorial library. For step-by-step setup, reference material, and ongoing updates, jump into the docs section.
WebRTC gathers ICE candidates so two peers can find a usable path. A host candidate can reveal a local interface address, so browsers can expose an mDNS hostname instead of a raw private address. RFC 8828 describes this privacy direction; it does not make a call anonymous or guarantee that every network path is hidden. BotBrowser can repeat an authorized call fixture and observe candidates and visible state. It cannot change a remote peer, grant media permission, or prove that a service retained no network data.
TL;DR
Treat an mDNS candidate as a privacy-preserving name for a local host candidate, not as proof that no local information exists. Record the browser release, ICE policy, candidate type, mDNS name, final origin, and visible application result separately. Test an owned control and candidate page with synthetic media and bounded waits. Never publish a discovered address, peer credential, or call recording.
Contents
- What mDNS changes
- Separate the evidence
- Run a bounded fixture
- Candidate types in context
- Design the comparison
- Interpret results conservatively
- Keep evidence useful and small
- Failure classification and ownership
- Review checklist
- Practical conclusion
What mDNS changes
An ICE host candidate describes a local interface that may be useful to a peer. With mDNS host candidates, the candidate contains a scoped hostname rather than the interface address. The browser and local resolver coordinate that name during connectivity checks. A server can still see signaling traffic, reflexive candidates can reveal a public mapping, and relay candidates can reveal that a relay was selected. The candidate list is therefore one observation in a larger connection, not a complete privacy report.
RFC 8828 is a threat-model and behavior document, not an application permission API. A secure context, user gesture, media permission, ICE server configuration, and browser implementation each have separate ownership. A page that cannot obtain a camera stream may still gather non-media ICE information, while a successful connection does not prove that no local metadata was exposed to an authorized peer.
Separate the evidence
| Observation | Capture | Does not prove |
|---|---|---|
| Signaling | final origin, response status, offer/answer direction | a peer is trusted |
| ICE candidates | type, protocol, mDNS label or redacted address, browser release | anonymity or no server logs |
| Connectivity | selected candidate pair and state transition | a stable call or quality guarantee |
| Media | synthetic track and visible play state | a real device or user consent |
| Application | owned status marker and timeout | a remote business record |
Do not turn an mDNS label into a hostname inventory. Redact candidate foundations, usernames, passwords, and raw SDP unless an authorized incident owner requires them. Keep the route and test identity synthetic. A policy change, browser update, or TURN configuration can change candidate types without changing the application's code.
Run a bounded fixture
Use two owned pages: a control with the baseline ICE policy and a candidate with the proposed browser or deployment setting. Use a generated audio/video track, a short signaling timeout, and an explicit icegatheringstate assertion. Classify DNS, certificate, signaling, permission, gathering, and connection failures separately. A timeout is missing evidence, not proof of a privacy property.
const result = await page.evaluate(async () => {
const pc = new RTCPeerConnection({ iceServers: [] });
const categories = [];
pc.onicecandidate = event => {
const candidate = event.candidate;
if (!candidate) return;
const isMdnsHost = candidate.type === 'host' && /\.local(?:\s|$)/.test(candidate.candidate);
categories.push(isMdnsHost ? 'mdns-host' : (candidate.type ?? 'unknown'));
};
const offer = await pc.createOffer({ offerToReceiveAudio: true });
await pc.setLocalDescription(offer);
await new Promise(resolve => setTimeout(resolve, 1500));
const result = {
state: pc.iceGatheringState,
categories,
};
pc.close();
return result;
});
The snippet is a local observation. Close the peer connection and context after each case. Store only the redacted result, browser release, fixture version, and timestamp. For a real call, the signaling service and peer owner remain responsible for authorization and retention.
Candidate review details
Candidate types in context
Candidate types answer different connectivity questions. A host candidate describes an address available on the local interface. A server-reflexive candidate represents an address and port as seen through a NAT mapping, usually learned through STUN. A relay candidate is allocated through TURN and carries traffic through a relay server. These labels are not rankings from private to public, and they do not state who can observe each value. Visibility depends on signaling, peer authorization, relay operation, and the exact browser implementation.
An mDNS host candidate substitutes a name for a local address in the candidate representation. The name is useful within the local connectivity process; it should not be treated as a stable identifier or resolved into a device inventory. RFC 8828 discusses mechanisms for limiting IP address exposure in WebRTC. It is useful to read that document as a threat-model guide: identify which address observers matter, what connectivity the application requires, and which privacy measures are compatible with that requirement. The document does not make all candidates identical, prohibit applications from using WebRTC, or promise that every browser and network combination produces the same candidate set.
The candidate list can change for ordinary reasons. A device may have several active interfaces. A VPN, network transition, firewall, STUN server, TURN server, enterprise policy, browser version, or permission decision can change what gathering produces. A candidate can be removed, redacted, or replaced before the application sees it. The selected pair can differ from the candidates initially gathered. Therefore compare runs only when the fixture records the relevant setup and avoid attributing a difference to mDNS until the browser release, network, ICE servers, and permission state are controlled.
Design the comparison
Write the question before collecting data. For example: “Does this declared browser release expose an mDNS host candidate to this owned test page when the interface and ICE configuration are held constant?” That question is narrow enough to answer with a browser observation. “Does this call hide the user's network identity?” mixes candidate representation, peer behavior, server logging, and data retention, none of which a single page can establish.
Use a control and a candidate that differ in one declared setting. Keep the same page origin, build, signaling endpoint, ICE server list, generated media, and browser permissions. Record the browser version and operating system category, but do not attach a persistent profile identifier. Preserve the exact test fixture revision and a short run identifier. If the route or configuration changes, start a new comparison rather than merging the results.
| Fixture field | Control | Candidate | Why it matters |
|---|---|---|---|
| Origin and route | same owned HTTPS route | same owned HTTPS route | avoids comparing different security contexts |
| Browser | declared release | declared release | implementations and defaults vary |
| ICE configuration | fixed synthetic setup | fixed synthetic setup | STUN/TURN changes affect candidate types |
| Media | generated track or none | generated track or none | no real device or user data is needed |
| Observation | redacted candidate category | redacted candidate category | prevents address and credential collection |
| Outcome | state and selected-pair category | state and selected-pair category | distinguishes gathering from connectivity |
The control is not a claim that an older behavior was safer, nor is the candidate automatically a regression. Both rows are observations under declared conditions. If a case needs an external peer, obtain authorization from its owner and use a test endpoint whose logs and retention are documented. Do not send candidate data to an unrelated public service just to see how it responds.
Interpret results conservatively
If a candidate string contains a .local name, record only that the page observed an mDNS-form host candidate. Avoid saving the full name because it is not needed to answer the fixture question. If the page receives no host candidate, that does not prove the interface address was never available elsewhere in the browser or operating system. If it receives a raw address, do not publish it; preserve it only under the authorized incident process, with access control and an expiration date.
Gathering completion and connection success are separate. iceGatheringState describes whether the browser is still collecting candidates. iceConnectionState and connectionState describe later connectivity progress. A completed gathering state does not guarantee a selected pair, and a connected state does not describe what the peer or signaling service retained. A failed connection might result from DNS, firewall rules, a relay outage, an invalid description, a remote peer failure, or an application timeout. Report the observed state transition and the first failure boundary instead of labeling every failure a privacy leak.
Similarly, do not infer media permission from candidate gathering. ICE negotiation can be configured in different ways, and the browser's media capture decision has its own permission and device requirements. An application can establish a data-channel connection without exposing camera or microphone content. A test that uses getUserMedia() must use a synthetic device or an explicit authorized test device, keep the permission state documented, and close all tracks at teardown. A browser's permission prompt is user-facing behavior and should not be suppressed to produce an artificial pass.
The signaling channel deserves its own review. Offers, answers, and ICE candidates often pass through an application server. HTTPS protects a transport hop but does not answer who can read server-side messages or how long they are stored. Use a test signaling service with an explicit owner, access boundary, and retention rule. Prefer short-lived fixtures and avoid logging full SDP. When the test needs to assert delivery, use an owned acknowledgement containing a run identifier rather than copying the candidate into a general application log.
Keep evidence useful and small
A useful receipt contains the question, fixture version, browser release, route, declared ICE configuration, candidate categories, completion state, selected-pair category when available, and a timestamp. It also lists what the receipt does not establish: peer-side observation, signaling retention, real-user consent, universal browser behavior, or anonymity. That negative scope prevents a future report reader from reusing a narrow result for a broader claim.
Keep raw network data out of screenshots and issue titles. Do not include local addresses, public mappings, candidate passwords, usernames, cookies, account names, or call recordings in an ordinary bug report. If raw data is essential to a security investigation, use the incident's approved restricted storage, assign an owner, and set a deletion date. The public article and general-purpose automation report should contain only redacted categories and synthetic route names.
When a release changes, compare the candidate categories and connection state, not byte-for-byte candidate strings. Foundations and component identifiers can differ without a meaningful change to the privacy behavior. Conversely, a candidate category can change while the visible call still works. The review should ask whether the change is expected, documented, and compatible with the product requirement, not whether every string stayed identical.
Before changing a production ICE policy, check the call's reliability needs, relay capacity, regional availability, latency budget, and fallback behavior with the relevant network owners. A stricter route can reduce one class of exposure while making connections fail for users behind restrictive firewalls. A relay can improve reachability but introduces another operator and another point with access to transport metadata. These are deployment tradeoffs, not settings to change blindly from a privacy test. Document who approved the decision and how to roll it back.
Failure classification and ownership
If navigation fails before the page loads, report an origin or network availability issue. If the signaling request fails, report its response and the owner of the test service. If candidate gathering completes with no usable candidate, investigate browser, network, and ICE configuration separately. If a candidate is present but no pair connects, distinguish peer reachability from relay and firewall conditions. If the connection succeeds but the application does not show completion, the remaining issue is an application assertion. This sequence gives each observation an owner and prevents a generic “WebRTC privacy failed” diagnosis.
Use bounded waits at every stage. A navigation timeout, signaling timeout, candidate timeout, or connection timeout should preserve the stage at which evidence stopped. Do not convert a timeout into a passing result or a claim that the browser blocked a candidate. Keep diagnostic text free of secret-bearing SDP. Close the peer connection, stop synthetic tracks, close the context, and confirm cleanup even when an assertion throws. A test harness that leaks a live connection can contaminate the next run and make the comparison unreliable.
If a browser update changes candidate behavior, first confirm that the expected route and configuration actually reached the browser. Then repeat the same test with a fresh context, inspect the redacted candidate categories, and compare the browser's documented behavior where available. A single run is a signal to investigate, not enough evidence for a compatibility guarantee. A repeated controlled observation can support a release note for that declared configuration while still leaving other versions and networks outside scope.
Review checklist
Before approving a WebRTC privacy test, answer these questions in the test record:
- Is the page and signaling endpoint owned or explicitly authorized?
- Is the question about candidate representation, connectivity, media, or service handling?
- Are the browser release, origin, ICE configuration, and synthetic inputs declared?
- Are names, addresses, credentials, SDP, and user media excluded from ordinary logs?
- Does each timeout identify the stage where evidence stopped?
- Is the result limited to the observed browser, route, and fixture revision?
- Does the report name the peer and service facts that remain unverified?
- Are connections and tracks closed on both success and failure?
If one answer is missing, make the uncertainty visible and assign an owner. Do not fill an evidence gap with a privacy promise. This is especially important when a public support article or compliance review reuses a test result; the reviewer should see the exact scope without needing to infer what the automation did.
Practical conclusion
Review WebRTC privacy as a chain: origin and secure context, permission state, ICE policy, candidate redaction, selected route, and application outcome. Re-run the control/candidate fixture after browser, TURN, signaling, or network changes. Use “observed mDNS candidate” and “raw address not recorded” when those are the actual facts. Do not say “the browser hides the network” or “the call is anonymous.”
BotBrowser can hold a declared context steady and repeat an authorized WebRTC journey. It cannot provide a camera, control a peer's logs, force a browser implementation, or establish universal RFC 8828 compliance. For adjacent reading see the WebRTC leak-prevention guide and UDP-over-SOCKS5 guide.
BotBrowser supports repeatable, authorized browser-context validation of a declared WebRTC journey, but it cannot control a remote peer, provide a camera, inspect service retention, or guarantee every browser's behavior. That boundary matters when a test is used in a release decision. The browser can report what it exposed to the page during one run; it cannot tell a team what a signaling service logged, what a peer inferred, or what happened on a different network. Keep those questions with their owners and link their evidence rather than expanding the browser report into a privacy certification.
The durable lesson is simple: mDNS changes how a host candidate is represented to the WebRTC application, while the rest of the call still has its own observers, permissions, routes, and owners. Keep those facts together in a small, redacted report. That report can guide a browser update review without claiming to describe every device, network, peer, or service that might participate in a production call.
Sources
- RFC 8828: WebRTC IP Address Privacy
- W3C WebRTC
- MDN RTCPeerConnection
- MDN WebRTC connectivity
- BotBrowser WebRTC leak prevention
BotBrowser Team
Related Articles
Take BotBrowser from research to production
The guides cover the model first, then move into cross-platform validation, isolated contexts, and scale-ready browser deployment.