LINKEDIN BROWSERGATE

📡 Security Affairs · 2026-04-27

LINKEDIN BROWSERGATE

BrowserGate is an investigation conducted by Fairlinked (https://browsergate.eu/), an association of commercial LinkedIn users, which documents what it describes as one of the largest data breach and corporate espionage scandals in digital history. The central thesis: every time one of the billions of users visits linkedin.com, hidden code scans the computer for installed software, collects the results, and transmits them to LinkedIn servers and third-party companies, including a US-Israeli cybersecurity firm.

The user is never informed nor asked for consent. LinkedIn’s privacy policy makes no mention of it.

The system consists of three cooperating modules within a single JavaScript bundle (Webpack chunk.905, ~2.7 MB, Ember.js framework):

System | Internal Name | Function |

APFC / DNAtriggerApfc, triggerDnaApfcEventDevice fingerprinting: 48 browser characteristics AEDAedEvent, fetchExtensionsActive extension scanning via fetch() SpectroscopySpectroscopyEvent, scanDOMForPrefixPassive DOM scanning

Inside Webpack module 75023, there is a hardcoded array with entries in the form {id: “…”, file: “…”} where *id* is the Chrome Web Store extension ID and *file* is a path to an internal extension resource declared as web-accessible.

The probing mechanism:

Chrome extensions can expose internal files to web pages through the web_accessible_resources field in their manifest.json. When an extension is installed and has exposed a resource, a fetch() request to *chrome-extension://{id}/{file}* will succeed. When it is not installed, Chrome blocks the request and the promise is rejected.

Method 1 — Parallel batch scan: All fetch() requests are launched simultaneously via Promise.allSettled(). Each request that resolves as “fulfilled” indicates that extension is installed.

Method 2 — Staggered sequential scan: An alternative that probes one extension at a time with a configurable delay (staggerDetectionMs) between each request. This allows LinkedIn to throttle the scan, reducing its visibility in network monitoring tools and its CPU impact.

Scale of the list: In December 2025 the array contained 5,459 entries. By February 2026 it had grown to 6,167. The array alone occupies approximately 409,000 characters of source code. LinkedIn added 708 extensions between December 2025 and February 2026 — roughly 12 new extensions per day.

Independently from AED, LinkedIn runs a second system that traverses the entire DOM tree looking for evidence of extension activity. Many Chrome extensions inject elements into web pages; when they do, the injected content often contains references to the internal URL scheme of extensions (chrome-extension://). Spectroscopy finds these references.

The function recursively inspects every node: for text nodes it checks whether the text contains *chrome-extension://*; for element nodes it checks every attribute value. When a match is found, it extracts the 32-character extension ID from the URL.

The complementarity of the two methods is by design:

Method | Technique | What it detects |

AEDfetch() on known resource pathsInstalled extensions, even if they inject nothing into the page SpectroscopyDOM tree walkExtensions that actively modify the page, even if not in the list

The APFC (Anti-fraud Platform Features Collection) system, also internally referred to as DNA (Device Network Analysis), collects 48 distinct browser characteristics, including: local IP address via WebRTC, connected devices (cameras, microphones, speakers) via enumerateDevices, number of CPU cores, device RAM, canvas fingerprint, WebGL fingerprint with 65+ parameters, AudioContext fingerprint, installed system fonts, battery status, network information, and detection of incognito mode, automation, and Do Not Track.

Feature #23 deserves attention: LinkedIn collects the user’s Do Not Track preference, then excludes it from the fingerprint hash. They record that you asked not to be tracked. Then they track you.

The fingerprint payload is serialized to JSON, encrypted with an RSA public key identified as *apfcDfPK*, and transmitted to two endpoints: */platformtelemetry/li/apfcDf* and */apfc/collect*. The encrypted fingerprint is also injected as an HTTP header in all subsequent API requests made during the user’s session: it is not sent just once, but accompanies every API call for the entire duration of the visit.

HUMAN Security (formerly PerimeterX): LinkedIn loads a hidden 0×0 pixel iframe from li.protechts.net, positioned at *left: -9999px* and marked *aria-hidden=”true”*, which reads and sets PerimeterX cookies (_px3, _pxhd, _pxvid, _pxcts) via cross-origin postMessage.

Merchant Pool: a separate device fingerprinting script is loaded from merchantpool1.linkedin.com, passing the user’s session cookie and a hardcoded instance ID.

Google reCAPTCHA v3 Enterprise: loaded on every page load with action “onPageLoad”.

Several implementation choices reveal that the system was designed to avoid detection: idle execution via requestIdleCallback (no visible performance impact), staggered probing to distribute thousands of requests over time, a 0×0px hidden iframe for HUMAN Security, silent error handling with empty catch blocks that log nothing to the console, and RSA encryption of the payload that makes the content unreadable even to those inspecting network traffic.

The scan reveals religious beliefs, political opinions, disabilities, and job-seeking activity of identified individuals. LinkedIn scans extensions that identify practicing Muslims, political orientation, tools for neurodivergent users, and 509 job search tools that expose who is secretly looking for work on the very same platform where their current employer can see their profile. Furthermore, LinkedIn scans over 200 products that directly compete with its own sales tools. Knowing each user’s employer, it can map which companies use which competing products — effectively extracting the customer lists of thousands of software companies from users’ browsers. On the DMA front: the scan list has grown from approximately 461 products in 2024 to over 6,000 by February 2026. The EU ordered LinkedIn to open its platform to third-party tools; LinkedIn built a surveillance system to find and punish every user of those tools.

This case demonstrates exemplarily how JavaScript in the browser is a first-class attack surface, not only for malicious actors but for legitimate commercial platforms. The technical vectors exploited — fetch() on chrome-extension://, DOM traversal, WebRTC IP leak, canvas/WebGL/audio fingerprinting, cross-origin iframe — are all legitimate browser APIs, not exploits. No CVE is required to conduct mass intelligence gathering on a billion users.

Which browsers are vulnerable? The primary vector — AED scanning via fetch() on chrome-extension:// — is architecturally Chromium-specific. The code performs an explicit check: if the browser does not report “Chrome” in the user agent string, the scan does not start. However, as is often the case with cyber topics of this kind, the real picture is more nuanced.

Browser | Engine | Vulnerable AED | Vulnerable Spectroscopy | Vulnerable APFC Fingerprint | Notes |

ChromeChromium/BlinkYesYesYesPrimary attack vector EdgeChromium/BlinkYesYesYesIdentical to Chrome for this vector BraveChromium/BlinkPartial*YesPartial*Shield anti-fingerprint degrades APFC Opera / ArcChromium/BlinkYesYesYes VivaldiChromium/BlinkYesYesYes FirefoxGeckoNo**Partial***Partialmoz-extension:// incompatible with LinkedIn list LibreWolfGeckoNoReducedMany blocked by defaultRFP active by default, recommended choice Tor BrowserGeckoNoVery reducedFingerprint resistantMaximum APFC protection Firefox ESRGeckoNoPartial***PartialManual hardening required Apple SafariWebKitNo****No*****Partial (ITP)macOS/iOS only — closed source Safari on iOSWebKit (mandatory)NoNoVery reducedAll iOS browsers use WebKit per App Store policy lynx / w3mNoneNoNoNoNo JS — no active vector

Table Notes

* Brave: blocks some cross-origin requests and has anti-fingerprinting shields that degrade APFC, but extensions still use chrome-extension:// — AED works unless shields are at maximum.

** Firefox — AED: uses moz-extension:// instead of chrome-extension://. LinkedIn’s hardcoded list (6,167 entries with Chrome Web Store IDs) is structurally incompatible. The userAgent.indexOf(“Chrome”) check fails as a second exclusion layer.

* Firefox — Spectroscopy:** if an extension injects elements into the DOM with references to moz-extension://, Spectroscopy does not find them because it only searches for the string chrome-extension://. De facto protection.

** Safari — AED:** double protection: user agent does not contain “Chrome” → module is not executed; the Safari extension URI scheme is safari-web-extension:// → incompatible with the list.

* Safari — Spectroscopy: Safari extensions do not inject URLs with the chrome-extension:// scheme into the DOM → no possible match.

APFC Feature | Safari | Reason |

WebRTC IP leak (#1)PartialSafari limits WebRTC but does not disable it Canvas fingerprint (#36)ITP adds noiseNot eliminated, degraded WebGL (#37)VulnerableAvailable and fingerprintable AudioContext (#42)PartialNoise added by Safari 17+ Battery API (#41)ImmuneApple never implemented the Battery Status API enumerateDevices (#2)PartialRequires explicit permission Font enumeration (#46-47)PartialITP limits, does not eliminate HUMAN Security iframeVulnerableCookie partitioned by ITP, but iframe loaded reCAPTCHA v3VulnerableLoaded on any browser

Safari deserves a separate analysis because it has a completely different extension architecture.

Safari does not use chrome-extension:// or moz-extension://. It uses a completely different scheme: safari-web-extension://

Up to Safari 13 (and earlier versions), extensions used yet another different scheme (safari-extension://). This means that LinkedIn’s hardcoded list — built entirely on 32-character Chrome Web Store IDs — is structurally incompatible with Safari.

The explicit check in the LinkedIn code confirms this:

function s() {

return window?.navigator?.userAgent?.indexOf(“Chrome”) > -1;

}

if (!a() || !s()) return; // exits if not Chrome

On Safari, navigator.userAgent does not contain the string “Chrome” — it contains “Safari” and “WebKit”. The check fails and the entire AED module is not executed.

Vector | Safari vulnerable? | Notes |

AED (fetch() on chrome-extension://)NoUser agent check fails + different scheme Spectroscopy (DOM walk for chrome-extension://)NoSafari extensions do not inject URLs with that scheme WebRTC IP leak (APFC #1)PartialSafari limits WebRTC, but does not disable it Canvas fingerprint (APFC #36)Yes with limitsITP adds noise, but does not eliminate the vector WebGL fingerprint (APFC #37)YesWebGL available and fingerprintable AudioContext (APFC #42)PartialSafari adds noise to AudioContext from Safari 17 enumerateDevices (APFC #2)PartialRequires explicit permission Font enumeration (APFC #46-47)PartialITP limits, does not eliminate Battery API (APFC #41)NoApple never implemented Battery Status API HUMAN Security iframeYesLoadable on any browser Google reCAPTCHA v3YesLoadable on any browser Merchant Pool scriptYesLoadable on any browser

Safari has a structural defense that other browsers lack by default: ITP (Intelligent Tracking Prevention), developed by Apple’s WebKit team. ITP operates at the engine level, not the extension level:

This does not directly block APFC, but significantly degrades the effectiveness of cross-session tracking. The HUMAN Security iframe (li.protechts.net) is loaded but its cookies are partitioned — it cannot correlate your identity across different sessions on different sites.

On iOS and iPadOS the situation is even more privacy-friendly:

On iPhone/iPad, BrowserGate is essentially inoperative on any browser.

Despite its advantages, Safari is not the optimal choice for all contexts:

✓ Pros | ✗ Cons |

• ITP is the best built-in anti-tracking system in a mainstream browser • No AED vulnerability by design • Battery API not implemented • Updated via OS updates — no patching delay• Available only on macOS and iOS — irrelevant for Linux/OpenBSD • Closed source — cannot be independently verified • Extensions very limited compared to Firefox • WebGL and canvas fingerprinting remain active vectors • ITP is not a substitute for Firefox’s privacy.resistFingerprinting

Safari is immune to BrowserGate’s primary vector (AED extension scanning) for solid architectural reasons: a different URI scheme and a user agent check that explicitly excludes non-Chromium browsers. ITP further reduces the effectiveness of cross-session fingerprinting.

And for LinkedIn in particular: any Firefox with a clean profile and RFP enabled beats Safari on APFC fingerprinting, because privacy.resistFingerprinting is more aggressive than ITP.

About the author: Pietro Cornelio

IT professional with 25+ years’ experience in electronics and CS, expert in system design, R&D, telecoms, and open-source.

Follow me on Twitter: @securityaffairs and Facebook and Mastodon

(SecurityAffairs – hacking, BrowserGate)


📌 来源: Security Affairs | 📅 2026-04-27

[!] CONTACT_CHANNELS

如需商务合作、技术咨询或漏洞反馈,请通过以下离岸节点联系作者。

> PING_AUTHOR (@A1RedTeam)