Skip to content
Back to research

The Silent Browser Fingerprint: Investigating AliExpress Step by Step

A Bluetooth fault exposed silent WebAudio processing on AliExpress. We first explain the discovery, the fingerprinting concern and its limits, then use an isolated Chromium profile, mitmweb, SHA-256, YARA, js-beautify, VS Code and a no-telemetry local reconstruction to validate the current Fireye capability step by step.

Follow along on GitHubWant to learn more about YARA?The official guide explains rule structure, strings and conditions such as “5 of them”.Read the YARA rule guide
YouTubeAliExpress Was Fingerprinting Your Browser With Silent Audio. Here's How I Proved ItOpen the related video

What happened, what was found and why it matters

This investigation did not begin with somebody hunting for fingerprinting code. It began with a physical fault: multipoint Bluetooth headphones stopped handing audio from a Windows PC back to a phone whenever an AliExpress tab was open. Closing the tab fixed it, but muting the tab did not. There was no visible or audible media to explain why [1] [8].

1. A Bluetooth symptom supplied the first clue

The PC behaved as if audio was active while AliExpress was open, even though the user could not hear anything.

2. Ordinary media was ruled out

The original investigator checked audio and video elements, media play calls, Media Session state, media requests and embedded frames. None explained the activity [1].

3. WebAudio was instrumented instead

Wrappers around AudioContext and AudioNode.connect recorded when JavaScript created a processing context and connected nodes to the system audio destination.

4. Two hidden contexts led to two scripts

Stack traces pointed to Alibaba AWSC Collina and Fireye bundles. Both created running audio graphs with no normal media element [1].

5. Source review explained the behaviour

The scripts generated a known waveform, measured its frequency output, set gain to zero and connected the graph to the destination. That can produce a browser clue while remaining silent.

The discovery chain matters because each action came from the previous observation. Our walkthrough begins after the original report and independently checks the current Fireye path rather than pretending we rediscovered the Bluetooth symptom.
The issue in one sentence

A shopping page could silently run a repeatable audio calculation, turn the returned values into a device clue and combine that clue with other browser measurements without asking for microphone access or showing a normal media player.

WebAudio fingerprinting is closer to a controlled benchmark than a recording. The page supplies the same mathematically generated signal to each browser. The browser processes it, an analyser returns a list of numbers, and small implementation or CPU-maths differences can change those numbers. The list can then be compared directly or shortened to a digest. No microphone is needed because the test signal is generated inside the browser [5] [12] [13].

StageWhat happensWhy it helps recognition
Known inputEvery browser receives the same sawtooth test signalCreates a fair comparison
Measured outputThe analyser returns 1,024 frequency-bin valuesCaptures processing differences
Comparable labelThe values are serialized or hashedMakes later comparisons practical
Composite profileAudio joins Canvas, WebGL, screen and hardware cluesNarrows the matching crowd
Later visitA similar profile appears after cookies changeMay support re-identification or a fraud score
One audio result is usually a clue, not a name. The privacy value comes from combining it with other clues.
Why this can be a privacy problem even when audio alone is weak

W3C guidance describes fingerprinting as identifying or re-identifying a browser from observable characteristics. It can correlate visits without the clear, resettable control a cookie provides [6]. The same capability can support legitimate fraud and bot detection, but the browser cannot reveal Alibaba’s purpose, retention period or server-side linkage. We will keep those possibilities separate from what our evidence proves.

This walkthrough is for somebody who can open a terminal and install a package but has not investigated browser fingerprinting or reverse-engineered minified JavaScript. We are testing a narrow claim: does a current public AliExpress visit serve Fireye JavaScript containing the complete WebAudio measurement capability, and what happens when we safely reproduce only that mechanism locally?

Evidence typeWhat it can establishWhat it cannot establish
Live network captureWhich scripts the public page served during this runWhat every visitor receives or what the server later decides
Static source reviewThe WebAudio operations and data handling present in the bundleThat the branch executed in this browser session
Local reconstructionHow the mechanism behaves without telemetryAlibaba’s server-side retention, linkage or purpose
Keeping these evidence types separate prevents a source-code capability from becoming an unsupported tracking claim.

Chromium 149

An isolated, logged-out browser profile gives us a clean public visit without personal history or account cookies.

mitmweb 12.2.3

The graphical interface for mitmproxy keeps the flow list, selected request, response headers and decoded JavaScript connected on one screen.

curl, wc and SHA-256

These extract the exact observed response into temporary storage, count its bytes and give it a stable integrity identifier.

YARA 4.5.8 and js-beautify 2.0.3

YARA quickly locates a family of relevant strings. The beautifier then turns one huge minified line into source windows a person can inspect.

Visual Studio Code and the local lab

VS Code preserves line numbers and syntax context. The Xposure lab recreates only the audio graph and sends nothing off the page.

Safety boundary

The live work is a normal read-only visit to a public page and its public JavaScript. The browser profile, proxy flow, extracted bundles and YARA package stay in temporary storage. We do not log in, submit transactions, bypass controls or retain cookies, identifiers, request payloads or complete third-party bundles in the repository.

Copy BASH
# Standard Kali setup. Review the package list before confirming.
sudo apt update
sudo apt install chromium mitmproxy yara curl coreutils python3-jsbeautifier

# Any editor with line numbers works. This walkthrough used VS Code 1.128.
Command partPlain-English meaningWhy we need it
sudo apt updateRefresh Kali’s package catalogueMakes the following package names resolve to current repository versions
sudo apt install …Install the listed tools after showing the package promptProvides the browser, proxy, triage and evidence commands used below
chromiumWeb browserCreates the isolated public visit
mitmproxyIncludes the mitmweb network-capture interfaceKeeps requests and responses connected visually
yaraPattern-matching command-line toolNarrows a large bundle to related WebAudio clues
curl and coreutilsDownload, byte-count and hash utilitiesExtract and identify one exact response
python3-jsbeautifierJavaScript formatterRestores line breaks and indentation to minified source
Setup command breakdown

Step 1: open the public target in an isolated browser

Why this step comes first

Before looking for suspicious code, we need to establish the exact public page and remove our normal cookies, account history and extensions from the observation. Expected result: a working, logged-out AliExpress homepage in a disposable browser profile.

Start with the ordinary site, not the news headline. An isolated Chromium profile is simply a fresh browser data directory created for this investigation. It prevents existing AliExpress cookies or logins from changing what we observe.

Copy BASH
profile=$(mktemp -d /tmp/aliexpress-browser.XXXXXX)
chromium --user-data-dir="$profile" --no-first-run https://www.aliexpress.com/
Command partWhat it meansWhy it is here
profile=$(mktemp -d …)Create a uniquely named temporary directory and store its path in profileGives Chromium fresh browser storage that can be removed later
XXXXXXPlaceholder characters that mktemp replaces safelyAvoids guessing a directory name or colliding with an existing one
chromiumStart the Chromium browserRuns the public visit we will observe
--user-data-dir="$profile"Use the new directory for cookies, cache and settingsKeeps personal browser data out of the capture
--no-first-runSkip Chromium’s welcome workflowPrevents setup screens obscuring the target
https://www.aliexpress.com/The public target URLStarts from the ordinary homepage named in the report
Step 1 command breakdown
  1. Confirm the public target: The address bar shows aliexpress.com over HTTPS. We are examining the normal public homepage, not a copied page or synthetic victim.
  2. Keep the acquisition logged out: The header still says Sign in / Register. That matters because personal account cookies and order history are outside this investigation.
  3. Begin with ordinary browsing: The page is functioning as a shopping homepage. Nothing visible tells the visitor that browser-security scripts will inspect WebAudio capabilities.

ObservedThe logged-out public homepage loaded successfully over HTTPS.

Next stepThe page alone cannot tell us which background script matters, so capture its network traffic and narrow the script inventory.

1. Confirm the address is aliexpress.com and that the profile is logged out. This establishes the public target and gives the next step a clean starting point.

Step 2: capture the JavaScript traffic with mitmweb

Why capture traffic now

The visible page cannot tell us which background file created the reported audio graph. A proxy records the files this browser actually requests, so the script target comes from evidence rather than a copied URL. Expected result: a small set of successful AliExpress JavaScript responses containing the reported Fireye family name.

A browser downloads HTML, images, fonts and JavaScript in the background. mitmweb is the browser interface for mitmproxy: it records those requests and lets us select one response without losing the surrounding network context. Use a disposable profile because the lab-only TLS interception must never be mixed with normal browsing.

Copy BASH
mitmweb   --listen-host 127.0.0.1 --listen-port 18080   --web-host 127.0.0.1 --web-port 18081   --no-web-open-browser

chromium   --proxy-server=http://127.0.0.1:18080   --ignore-certificate-errors   --user-data-dir="$profile"   https://www.aliexpress.com/
Command partWhat it meansWhy it is here
mitmwebStart mitmproxy’s browser-based interfaceLets us keep the flow list, headers and response body together
--listen-host 127.0.0.1Accept proxy traffic only from this machineAvoids exposing the lab proxy to the network
--listen-port 18080Put the traffic proxy on local port 18080Gives Chromium one explicit proxy address
--web-host 127.0.0.1 --web-port 18081Put the mitmweb interface on a separate local portKeeps the analyst interface local and distinct from browser traffic
--no-web-open-browserDo not launch another browser automaticallyPrevents an unrelated profile entering the capture
--proxy-server=http://127.0.0.1:18080Send this Chromium profile through mitmwebMakes its requests visible in the flow list
--ignore-certificate-errorsAccept the disposable proxy’s lab certificateAllows HTTPS inspection only inside this temporary profile
Step 2 command breakdown
Why the certificate exception is bounded

The exception trusts this local proxy only inside the temporary browser profile. Do not use that browser for email, passwords, administration or ordinary browsing. A longer-lived lab should import the mitmproxy CA into an equally isolated profile instead.

  1. Reduce hundreds of flows to one script: The search field contains fireyejs. mitmweb translates that into a case-insensitive URL filter and leaves one matching flow.
  2. Derive the exact script URL: The surviving row names assets.aliexpress-media.com, the AWSC/fireyejs family, version 1.231.67 and fireyejs.js. We do not have to guess a filename.
  3. Confirm a successful JavaScript response: GET, HTTP 200, 199.9 KB compressed and 50 ms show that this was a real response used by the page, not a failed or blocked request.

ObservedOne current Fireye response was selected from 303 captured browser flows.

Next stepOpen the selected response to confirm its content type and inspect the decoded body before extracting anything.

2. Type fireyejs into mitmweb Search. The 303 captured flows reduce to one exact AWSC script. Its visible URL gives us the extraction target for the next steps.

Step 3: inspect the selected Fireye response

Why inspect before downloading

A filename alone is weak evidence. We first need to prove that the selected browser flow succeeded and returned JavaScript, then keep that identity attached to the body. Expected result: HTTP 200, a JavaScript content type and a decoded but difficult-to-read response.

Selecting a flow joins three facts: the page requested this URL, the server returned it successfully, and the response body is JavaScript. That chain is stronger than downloading a URL copied from somebody else’s report.

  1. Keep the network identity attached: The left pane keeps the selected URL, method, status, compressed size and timing visible while we inspect its response.
  2. Open the actual response: Response is selected rather than Request. This avoids confusing browser request headers with evidence returned by the JavaScript host.
  3. Verify status and content type: HTTP/2 200 and content-type application/javascript prove the selected object is a successful JavaScript response.
  4. The proxy decoded the gzip body: The bottom-right pane labels the body JavaScript [Decoded gzip]. The source is present, but its state machine and one-line layout make a dedicated extraction and beautification necessary.

ObservedThe selected HTTP 200 response is gzip-compressed JavaScript from the exact Fireye version loaded by the page.

Next stepExtract that observed URL into temporary storage and hash it so every later code screenshot is tied to one exact response.

3. Select the Fireye row and open Response. Read the status and content type first, then the decoded JavaScript pane. The response is genuine but heavily obfuscated, so visual reading alone is not enough.

Step 4: extract and fingerprint the evidence file

Why identify the file before analysing it

The live URL can change after our visit. We need one temporary copy plus two identifiers—the decoded byte count and a SHA-256 digest—so every later line number, YARA match and screenshot refers to the same bytes. Expected result: one saved JavaScript file, one byte count and one 64-character digest.

Here “fingerprint” means a file integrity digest, not browser tracking. Think of SHA-256 as a tamper-evident label calculated from every byte. It does not encrypt the file and cannot reconstruct it. If one byte changes, the label should change; matching labels give us strong evidence that two copies contain the same bytes [15].

Copy BASH
url='https://assets.aliexpress-media.com/g/AWSC/fireyejs/1.231.67/fireyejs.js'
curl --fail --silent --show-error --compressed   "$url" --output /tmp/alibaba-fireye.js
wc -c /tmp/alibaba-fireye.js
sha256sum /tmp/alibaba-fireye.js
Command partWhat it meansWhy it is here
url='https://…/fireyejs.js'Store the exact observed URL in a shell variableAvoids retyping or silently changing the acquisition target
curlCommand-line HTTP clientDownloads the same public response the browser requested [14]
--failReturn an error for HTTP failure responsesStops a 404 or server error page becoming our “JavaScript evidence”
--silent --show-errorHide the progress meter but keep useful errorsProduces clean evidence without concealing a failed download
--compressedRequest compressed transfer and automatically decode itSaves readable JavaScript bytes rather than gzip-compressed bytes
"$url"Read the stored URL as one quoted valuePrevents shell splitting or accidental target changes
--output /tmp/alibaba-fireye.jsSave the response under a temporary filenameCreates the bounded local evidence file used below
wc -cCount bytes; wc means “word count” and -c selects bytesExplains why the decoded file is 462,779 bytes [16]
sha256sumCalculate the file’s SHA-256 digestBinds later analysis to this exact byte sequence
Step 4 command breakdown
  1. Download only the observed object: The curl URL is copied from the selected mitmweb flow. --compressed asks curl to decode gzip so the saved bytes match the JavaScript we will analyse.
  2. Record the decoded size: wc reports 462,779 bytes. mitmweb showed the smaller compressed transfer size; both can be correct because gzip changes the number of bytes sent over the network.
  3. Bind the rest of the review: The complete SHA-256 identifies this exact decoded file. Later line numbers and screenshots can be checked against the same digest.

ObservedThe current Fireye response was extracted to temporary storage and bound to one byte count and SHA-256.

Next stepThe file has more than 460 KB of obfuscated code. Use a triage signature to locate the WebAudio capability before reading source line by line.

4. Run the extraction and integrity commands in Kali. Record both outputs before analysis: 462,779 decoded bytes and SHA-256 01a202f4…668632.

Step 5: triage the bundle with YARA

Why use YARA before reading the source

The evidence file is more than 460 KB and deliberately difficult to read. YARA gives us a transparent shortlist of related WebAudio words and their byte offsets. Expected result: several graph-building terms plus the analyser-read operation, which is enough to justify focused source review—not enough to claim the code executed.

YARA is a pattern-matching tool used in malware analysis and incident response, but it can scan any file. A rule gives names to strings and defines how many must appear before reporting a match. It is acting like a repeatable search checklist here, not labelling AliExpress as malware [11].

We require five of seven clues because one term is ambiguous: AudioContext can power a game or music player, and createGain can simply control volume. Five related terms are more likely to describe one complete path—create a context and signal, create measurement and processing nodes, then read frequency data. Requiring all seven would be brittle if a future minifier hid one name; requiring only one would produce noisy matches. The threshold is an analyst’s triage choice, not proof of maliciousness or execution.

Create a new text file at /tmp/webaudio-fingerprint.yar, paste the rule below and save it. The .yar extension is a convention; YARA reads the rule contents rather than trusting the filename.

Copy YARA
rule WebAudio_Fingerprint_Capability
{
  meta:
    boundary = "String presence is not proof of runtime execution"

  strings:
    $context = "AudioContext" ascii
    $oscillator = "createOscillator" ascii
    $analyser = "createAnalyser" ascii
    $gain = "createGain" ascii
    $processor = "createScriptProcessor" ascii
    $frequency_data = "getFloatFrequencyData" ascii
    $waveform = "sawtooth" ascii

  condition:
    5 of them
}
Rule partPlain-English meaningWhy it matters
rule WebAudio_Fingerprint_CapabilityGive this search checklist a readable nameMakes the result self-explanatory
metaAnalyst notes that do not affect matchingKeeps the “presence is not execution” boundary beside the rule
stringsThe named text clues to search forRecords exactly what the analyst considered relevant
asciiLook for ordinary one-byte textMatches the method names visible in this JavaScript bundle
$context … $waveformSeven clues covering setup, graph nodes and measurementLooks for a coherent capability rather than one scary word
5 of themMatch when any five or more named strings are presentBalances tolerance to obfuscation against noisy one-string matches [11]
How to read the YARA rule
Copy BASH
yara --print-strings   /tmp/webaudio-fingerprint.yar   /tmp/alibaba-fireye.js
Command partWhat it meansWhy it is here
yaraRun the YARA command-line scannerApplies our rule without executing the JavaScript
--print-stringsShow each matched string and its hexadecimal byte offsetProvides pivots into the large file instead of only a yes/no result
/tmp/webaudio-fingerprint.yarThe rule file we just explainedMakes the search criteria reproducible
/tmp/alibaba-fireye.jsThe SHA-bound evidence file from Step 4Keeps the scan limited to one known object
How to read the YARA command
Why our screenshot says /tmp/yara

The VM already had the YARA analysis library but not its command-line frontend. Rather than change the machine globally, we downloaded the signed Kali yara package, unpacked its CLI into temporary storage and ran /tmp/yara. A normal sudo apt install yara installation uses the shorter yara command shown above and runs the same engine.

  1. Run one explained rule against one file: The command names the rule and the SHA-bound Fireye file explicitly. No directory-wide or live-site scanning occurs.
  2. Several related WebAudio APIs are present: AudioContext, oscillator, analyser, gain and ScriptProcessor strings appear together. That is enough to justify source review, but not yet a fingerprinting conclusion.
  3. Find the data-collection operation: getFloatFrequencyData is the analyser read we need to follow. Its hexadecimal offset leads to the routine that handles returned frequency bins.
  4. Two sawtooth locations deserve comparison: Two sawtooth matches suggest more than one graph-building branch. Beautifying the file will reveal whether their structures agree.

ObservedThe bundle matches a multi-string WebAudio capability rule, including two sawtooth locations and one frequency-data read.

Next stepString presence is not execution. Beautify the exact file and inspect the operations around those matches in context.

5. Run YARA with string output enabled. It finds the API family, one frequency-data read and two sawtooth locations. Those offsets tell us where to inspect after beautification.

Step 6: beautify the code and read the audio graph

Why turn the YARA hits into readable code

YARA proved that related words exist but not how they connect. We need the surrounding operations in execution order to distinguish an ordinary audio feature from a silent measurement path. Expected result: one readable source window that creates a signal, measures it, routes it through zero gain and attaches a processing callback.

Minification removes whitespace and shortens names so a browser downloads less data. Obfuscation goes further by hiding method names and control flow. js-beautify cannot undo the state machine, but it restores line breaks and indentation so related operations fit on one screen.

Copy BASH
js-beautify   -o /tmp/fireyejs.beautified.js   -s 2   /tmp/alibaba-fireye.js

grep -nE 'createOscillator|getFloatFrequencyData|sawtooth'   /tmp/fireyejs.beautified.js

code --goto /tmp/fireyejs.beautified.js:3980:1
Command partWhat it meansWhy it is here
js-beautifyFormat JavaScript without executing itTurns one minified line into navigable source
-o /tmp/fireyejs.beautified.jsWrite a separate formatted copyPreserves the original SHA-bound file unchanged
-s 2Indent nested code by two spacesFits more useful context on screen while keeping structure visible
/tmp/alibaba-fireye.jsRead the evidence file from Step 4Keeps the transformation tied to the known response
grep -nESearch with line numbers and an extended patternTranslates YARA’s byte-offset clues into readable locations in the formatted copy
term1|term2|term3The vertical bars mean “match any of these terms”Finds graph creation, analyser collection and waveform locations in one pass
code --goto file:3980:1Open VS Code at line 3980, column 1Jumps from the YARA-led review to the first relevant graph window
Step 6 command breakdown
Why some VS Code text is blue

The blue background is the analyst’s manual text selection, not a warning or automatic detection from VS Code. It marks the exact obfuscated lines translated below; the red boxes separate node creation from connection and silence.

  1. Keep the reviewed file and lines visible: VS Code shows fireyejs.beautified.js and line numbers 3980–3985, making this window reproducible against the earlier SHA-256.
  2. Create the signal and measurement nodes: The blue selection is the analyst’s VS Code highlight. These lines call createOscillator, createAnalyser, createGain and a dynamically assembled createScriptProcessor with 4096, 1, 1 arguments.
  3. Silence the end, not the measurement: Line 3985 sets gain value 0 and waveform sawtooth, then connects oscillator → analyser → processor → gain → destination. The analyser has already received the signal before it becomes silent.

ObservedOne Fireye branch builds the complete real-time WebAudio graph reported by the original investigator.

Next stepA graph can process audio without fingerprinting. Follow the analyser read and see what the code does with the returned values.

6. Open beautified lines 3980–3985. The blue selection is deliberate analyst highlighting. Read it in order: create four nodes, choose a sawtooth signal, set the final gain to zero, connect the path and assign the processing callback.
Code cluePlain-English jobWhy it matters
createOscillator()Generate a predictable signal inside the browserNo microphone or sound file is required
oscillator.type = "sawtooth"Choose a waveform rich in multiple frequenciesGives the analyser a repeatable test pattern
createAnalyser()Measure the signal by frequencyProduces the numeric values followed in Step 7
createScriptProcessor(4096, 1, 1)Run JavaScript when a 4,096-sample audio block is processedProvides the callback that collects the measurement
createGain(); gain.value = 0Set the final output volume to silenceThe analyser still sees the signal before this final mute point
oscillator → analyser → processor → gain → destinationConnect the nodes into one live processing graphExplains both the silent measurement and the reported active audio path
What the selected code is doing
Follow the arrows left to right. The same known signal enters every browser; the analyser turns it into 1,024 numbers before the zero-gain node silences the destination. The output can vary with browser and CPU maths, so the numbers become a clue rather than recorded sound.

What stays the same

The JavaScript, sawtooth waveform and requested graph shape give browsers the same test.

What can differ

Browser audio code, operating system routines and CPU floating-point behaviour can alter tiny parts of the calculated output.

Why zero gain does not stop measurement

The analyser reads upstream. Turning the final volume to zero prevents audible output but does not erase values already collected.

What this does not do

It does not listen through the microphone. The signal is generated and measured inside the browser [12] [13].

Copy JAVASCRIPT
// Plain-English reconstruction of the selected source window
const oscillator = context.createOscillator();
const analyser = context.createAnalyser();
const gain = context.createGain();
const processor = context.createScriptProcessor(4096, 1, 1);

gain.gain.value = 0;
oscillator.type = 'sawtooth';
oscillator.connect(analyser);
analyser.connect(processor);
processor.connect(gain);
gain.connect(context.destination);

Step 7: follow the analyser values

Why the analyser output is the deciding pivot

Building an audio graph is not automatically fingerprinting; games and visualisers do it legitimately. We now ask whether Fireye reads the calculated output and turns it into something comparable. Expected result: an array sized to the analyser, a frequency-data read and a loop that joins every returned value.

A frequency bin is one slot covering a small part of the frequency range—like one bar in a music visualiser. For this graph, frequencyBinCount gives 1,024 slots. getFloatFrequencyData copies one decibel value into each Float32Array slot [12]. Fireye then loops over those values and joins them with a separator. That collection behaviour moves the evidence from “audio exists” to “the output is being measured and made comparable.”

  1. Navigate to the YARA-derived location: The selected file remains the SHA-bound beautified Fireye response, now at lines 2434–2435 near the getFloatFrequencyData match.
  2. Do not over-link the transport capability: sendBeacon appears at line 2420 in the surrounding state machine. Presence proves transport capability, not that this call received or sent the audio-bin string.
  3. Allocate and fill every frequency bin: The reversed text resolves to frequencyBinCount. The code creates the array and calls getFloatFrequencyData to populate it.
  4. Turn the measurements into one serial value: The loop continues while the index is below y.length and appends each bin plus a separator. That creates a value suitable for later comparison or inclusion in a larger fingerprint.

ObservedThe current served Fireye source contains the graph builders and a routine that reads and serializes every analyser bin.

Next stepStatic source proves capability, not runtime execution. Recreate only the documented graph locally and observe its result without Alibaba telemetry.

7. Open beautified line 2434. The selected routine allocates one value per frequency bin, calls getFloatFrequencyData and concatenates all returned values. A sendBeacon capability is visible elsewhere in the same state machine, but this window does not prove that beacon carried the audio values.
Copy JAVASCRIPT
const bins = new Float32Array(analyser.frequencyBinCount);
analyser.getFloatFrequencyData(bins);

let serialised = '';
for (const value of bins) {
  serialised += value + separator;
}
CodePlain-English meaningWhy it matters
analyser.frequencyBinCountAsk how many frequency slots the analyser returnsSets the expected measurement length—1,024 here
new Float32Array(…)Reserve one decimal-number slot for every binCreates the container that will hold the result
getFloatFrequencyData(bins)Copy current decibel values into that arrayThis is the actual measurement operation [12]
for (const value of bins)Visit every returned numberShows the code uses the complete measurement rather than one display value
serialised += value + separatorAppend each number and a delimiter to one stringProduces a consistently ordered value that can be compared or combined later
Translate the analyser routine line by line
Why the nearby sendBeacon line is kept separate

sendBeacon can transmit data, but seeing it elsewhere in an obfuscated state machine does not prove this audio string reached that call. We would need a runtime data-flow or a matching outbound payload to establish that connection. This walkthrough did not observe either, so it reports transport capability and audio measurement separately.

What the live source review does not prove

Our instrumented headless browser did not observe the AudioContext branch execute. The branch may depend on user activation, risk state, browser features or another environment decision. We also did not prove which outbound request, if any, carried the serialized audio values. The current source proves the complete capability; those runtime and transport claims remain unobserved.

Step 8: reproduce only the mechanism locally

Why reproduce the graph instead of running Alibaba code

Static review shows capability but our live run did not observe the branch execute. A minimal local page lets us test the calculation without executing the wider obfuscated bundle or sending results anywhere. Expected result: five measurements from the same browser, visible cleanup after every run and an honest count of repeated versus different results.

A safe reconstruction lets us answer a narrower question: does this browser return a repeatable result when we run the documented graph? Our page contains no Alibaba code, no Canvas or hardware collection, no microphone request and no network telemetry. It stops the oscillator and closes the audio context after every run.

The lab hashes each 1,024-number result only to give the long array a short comparison label. If two labels match, the underlying serialized arrays matched; if the labels differ, at least one measured value differed. The digest does not make the browser result more unique or stable—it only makes the five-run comparison readable.

  1. Verify the lab does less than the target code: The top strip states microphone never requested, output gain zero, no demo telemetry and explicit stop plus close cleanup.
  2. Run the same minimum graph: The left pane lists oscillator, analyser, processor and zero-gain destination in the same order recovered from Fireye.
  3. Read every result, not only the convenient one: The right pane shows two distinct digests across five runs and a 3/5 dominant group. That demonstrates partial repeatability and real within-browser variation.

ObservedFive local runs returned two digests, with the dominant result appearing three times and no external request made by the demo.

Next stepThe result is a stable clue in some runs, not a unique identity. Put it beside the other browser signals and explain the practical impact.

8. Select Run five times. This Chromium session produced two result groups; the most common digest appeared three times. The variation is evidence too, not a failed screenshot.
Local observation

Repeatable can still be fickle

The same browser returned two hashes across five identical calculations. One appeared twice and the other appeared three times.

What this proves

A service can treat related values as clues, but a single raw audio digest should not be presented as a unique or permanent device identity. Academic work reached the same conclusion: audio is less diverse alone and more useful when combined with other signals [5].

Step 9: connect the fingerprint to real-world impact

Why the final technical result is not the impact

A 64-character digest means little to a shopper. We now place the audio clue beside the other reported signals and ask what a service could do when a later visit has no old cookie. Expected result: a clear distinction between possible correlation, legitimate fraud scoring and the server-side use we did not observe.

A browser fingerprint works like narrowing a crowd. “Uses Chromium” describes millions of people. Add operating system, screen size, GPU renderer, Canvas output, processor count, WebRTC behaviour and an audio result, and the matching group can become much smaller. Audio does not need to identify a person by itself to add useful information.

SignalWhat variesWhy it can help recognition
WebAudioBrowser processing and broad CPU maths differencesAdds a processing clue
CanvasFonts, rasterisation and graphics outputCreates a rendering clue
WebGLGPU, driver, renderer and shader behaviourNarrows hardware/software groups
Screen and hardwareViewport, pixel ratio, memory and processor countAdds configuration clues
InteractionMouse, touch, focus, scrolling and automation patternsAdds behavioural and bot clues
Combined profileThe intersection of all observed valuesCan remain useful after cookies change
The wider Fireye and Collina investigation reported these signal families. Our live walkthrough directly revalidated the Fireye WebAudio capability, not every family below [1, 9, 10].
A practical example

A shopper clears cookies or opens a private window. The site cannot read the old cookie, but the new visit may still present a similar GPU, screen, Canvas, WebAudio and interaction profile. That profile could help a fraud system recognize a familiar device, or it could help correlate browsing without the transparency of a normal cookie. The browser evidence cannot tell us which server-side use Alibaba chose.

The cookie is an explicit stored identifier; the fingerprint is a similarity calculation. Clearing the cookie removes that one identifier, but a later visit may still present a close combination of audio, graphics, screen and hardware clues. The server chooses what to do with that candidate match—we did not observe Alibaba’s choice.

The audio mechanism also had a physical side effect. The gain was zero, but the graph connected to the real-time audio destination and did not cleanly close every source. On the affected Windows and Firefox setup, that kept the computer side of multipoint headphones active and stopped them handing audio back to a phone. An older Mozilla report also found the lingering request could prevent idle sleep [1] [3].

Research involving 2,093 participants found only 95 distinct audio fingerprints, while also showing audio added information to established fingerprinting vectors [5]. Firefox later reduced most WebAudio diversity to a few broad result groups, largely reflecting CPU instruction differences [4]. The honest conclusion is therefore narrower than the headline: the audio test is real, but the composite profile is the more important privacy object.

Step 10: protect users, clean up and retain only the evidence

Why mitigation and teardown are part of the investigation

The reader now understands the capability and its limits. The final step turns that knowledge into user and developer actions, then removes the temporary browser, full third-party source and raw capture. Expected result: only reviewed screenshots, small explained excerpts, hashes and the no-telemetry local demonstration remain.

Our recommended user-side control

Use Firefox with Mozilla’s recommended uBlock Origin extension, then add four narrow network filters: one Collina and one Fireye rule for each delivery host observed in our runs [17]. Firefox also reduces WebAudio fingerprinting variation, while the filters stop matching scripts before delivery [4] [19]. The narrow scope avoids blocking either CDN wholesale.

Video preflight found a host change

The original Chromium capture used assets.aliexpress-media.com and Fireye 1.231.67. During the approved-video preflight, Firefox received Fireye 1.234.20 from g.alicdn.com instead [9] [20]. The original two filters did not match that newer host. This is direct evidence for re-testing a mitigation rather than assuming a wildcard version alone is enough.

Copy TEXT
! AliExpress AWSC families across both delivery hosts observed in this investigation
||assets.aliexpress-media.com/g/AWSC/uab/*/collina.js$script,domain=aliexpress.com
||assets.aliexpress-media.com/g/AWSC/fireyejs/*/fireyejs.js$script,domain=aliexpress.com
||g.alicdn.com/AWSC/uab/*/collina.js$script,domain=aliexpress.com
||g.alicdn.com/AWSC/fireyejs/*/fireyejs.js$script,domain=aliexpress.com
Filter partPlain-English meaningWhy it is narrow
||assets.aliexpress-media.comAnchor two rules to the host captured in the main Chromium investigationCovers the /g/AWSC path without blocking the whole host
||g.alicdn.comAnchor two rules to the newer host observed in Firefox video preflightCovers the current /AWSC path without blocking the whole CDN
/g/AWSC/uab/ … collina.jsMatch the reported Collina family pathTargets one named script family
/g/AWSC/fireyejs/ … fireyejs.jsMatch the reported Fireye family pathTargets the second named family
*Allow the version directory to changeCovers a new version only when the surrounding family path still matches
$scriptApply only to JavaScript requestsDoes not block images, styles or the whole page
domain=aliexpress.comApply when AliExpress is the requesting siteAvoids a global rule for every site [18]
How to read the four uBlock Origin filters

Install from Mozilla Add-ons

Use the official uBlock Origin listing by Raymond Hill, not a similarly named copy [17].

Open Dashboard → My filters

Paste all four rules, select Apply changes, then close every existing AliExpress tab.

Open a new AliExpress tab

The new page load is important because blocking a request cannot stop an AudioContext that an old tab already created [1].

Expect a possible trade-off

These bundles appear connected to anti-abuse controls, so login or checkout may show more CAPTCHAs or fail. Temporarily disable the custom rules if they block a legitimate transaction [1].

The next three genuine Firefox views prove the extension state, saved rules and current network result. The uBlock logger shows Fireye 1.234.20 from g.alicdn.com blocked as a script request. Collina did not appear in that Firefox run, so the protected claim for this lane is one observed Fireye block—not two.

  1. Use the real extension: Firefox identifies uBlock Origin and shows Mozilla’s recommended-extension trophy beside it.
  2. Check the enabled state: The extension is under Enabled and its switch is on before any protected visit begins.

ObserveduBlock Origin is installed, recommended by Mozilla and enabled in this clean Firefox profile.

Next stepOpen My filters and verify both observed delivery hosts are covered.

10A. Confirm the real extension before testing. Firefox lists uBlock Origin under Enabled, shows the recommended-extension trophy and displays its switch in the on position.
  1. Custom filters are enabled: The checked setting allows the four narrow user rules below to participate in request filtering.
  2. Cover the original host: The first pair covers the AliExpress Media host captured in the main investigation.
  3. Cover the current Firefox host: The second pair covers g.alicdn.com, observed serving Fireye 1.234.20 during video preflight.
  4. Applied, not merely typed: Apply changes is disabled after saving, showing there is no pending unsaved edit.

ObservedFour narrow custom filters are enabled and saved.

Next stepClose old target tabs, open a fresh AliExpress tab and inspect the uBlock logger.

10B. Apply all four rules. The first pair covers the original AliExpress Media host; the second covers the newer g.alicdn.com host. The disabled Apply changes button shows the displayed edit is already saved.
  1. Filter the logger to Fireye: The logger search leaves the decisive Fireye network event visible without unrelated traffic.
  2. Read the blocking rule: The red row begins with the g.alicdn.com rule and --, uBlock’s blocked-request result.
  3. Confirm the current script path: The blocked request is Fireye 1.234.20 from the newer g.alicdn.com delivery host.

ObserveduBlock Origin blocked the current Fireye 1.234.20 script request before delivery in this Firefox profile.

Next stepUse the independent verifier as a repeatable second lane and retain the profile-scoped claim boundary.

10C. Open a new AliExpress tab, then filter the uBlock logger to fireyejs. The red row shows the current g.alicdn.com Fireye 1.234.20 script request blocked by the saved network rule.

For repeatable evidence, our independent Chromium verifier mirrors both family boundaries across both observed delivery hosts. It records only matching script-family events and instruments AudioContext before the page loads. It supports the control result, but it is not presented as proof that uBlock Origin itself was enabled; the Firefox views above provide that evidence.

Copy BASH
node content/forensics/verify-aliexpress-script-blocking.mjs
  1. Run the bounded verifier: The local script opens one fresh Chromium session and intercepts only the two exact reported script families.
  2. Name both controls: Collina and Fireye are configured separately so one successful block cannot hide a missed family.
  3. Read the observed blocks: Two script requests were blocked and no AudioContext was observed in this protected run.
  4. Keep the claim profile-scoped: Mitigated means this profile and run. It does not claim AliExpress changed or future paths will match.

ObservedThe fresh protected visit returned HTTP 200 while two matching script requests—Collina and Fireye—were blocked; no AudioContext was observed.

Next stepRecord the scope limits, keep browser-level fingerprinting protection enabled and remove all temporary acquisition material.

10D. Run the independent protected-profile verifier. Both configured AWSC families were requested as scripts and blocked in that Chromium lane. Zero AudioContexts were observed afterward. This is a separate repeatable check, not a substitute for the uBlock Origin evidence above.
ClaimResultBoundary
Current Firefox Fireye request ran after enabling the rulePreventedThe uBlock logger shows Fireye 1.234.20 blocked before delivery
An AudioContext appeared after blockingNot observedZero was recorded during this protected run
A future version under the same family pathsCovered by the wildcardRe-test because this preflight proved the host or path can change
All browser fingerprinting on AliExpressNot establishedOther scripts and passive signals remain possible
AliExpress changed globallyNot claimedThe control exists only in the user’s protected browser profile
What “mitigated” means in this evidence
  • Keep Firefox fingerprinting protection enabled as a second layer; script blocking and output normalisation address different parts of the problem [19].
  • Site owners should collect only the signals needed for a named security decision, disclose purpose and retention, and run invasive checks at the point of risk rather than silently on a general homepage [6].
  • WebAudio users should stop oscillators, disconnect nodes and close one-shot audio contexts immediately after the legitimate work finishes.
BeforeIncomplete cleanup shape

Disconnecting output nodes alone can leave a source or context alive.

Copy insecure JAVASCRIPT
analyser.disconnect();
processor.disconnect();
gain.disconnect();

// oscillator.stop() missing
// context.close() missing
AfterResponsible cleanup

Stop the source and close the context as soon as the one-shot operation completes.

Copy secure JAVASCRIPT
oscillator.stop();
oscillator.disconnect();
analyser.disconnect();
processor.disconnect();
gain.disconnect();
await context.close();
LinePlain-English meaningWhat it prevents
processor.onaudioprocess = nullRemove the JavaScript audio callbackStops further measurement code running
oscillator.stop()Stop the generated sourceEnds the live signal instead of only detaching later nodes
node.disconnect()Remove each connection in the graphReleases the routing path and object references
await context.close()Shut down the one-shot AudioContextReturns the browser and system audio resources promptly
Why each WebAudio cleanup line exists
Copy BASH
# Close Chromium, mitmweb and VS Code first.
rm -f -- /tmp/alibaba-fireye.js   /tmp/fireyejs.beautified.js   /tmp/webaudio-fingerprint.yar

if [[ -n "$profile" && "$profile" == /tmp/aliexpress-browser.* ]]; then
  rm -rf -- "$profile"
fi
Command partWhat it meansWhy it is here
rm -f -- file1 file2 file3Remove the three named temporary analysis files; -- ends option parsingDeletes only the bounded files listed explicitly
if [[ … ]]; thenRun the profile deletion only when both safety checks passPrevents an empty or unexpected path being removed
-n "$profile"Confirm the variable is not emptyRejects an unresolved deletion target
"$profile" == /tmp/aliexpress-browser.*Confirm the path matches our temporary-profile prefixKeeps recursive removal inside the directory family created in Step 1
rm -rf -- "$profile"Recursively remove that validated disposable browser directoryDeletes cookies, cache and the temporary proxy trust state
Teardown command breakdown
What remains after teardown

The repository keeps reviewed screenshots, their annotations, non-sensitive response metadata, byte counts, hashes, small source excerpts and the no-telemetry reconstruction. The disposable Chromium profiles, raw mitmproxy flow, complete Fireye and Collina bundles, YARA package, cookies and request payloads are removed.

The lesson to carry forward

Do not stop at a scary API name. Start from an observed request, bind the exact evidence, use a detection tool to narrow the search, read the operations in context, reproduce only the necessary mechanism, report negative findings and finish with the practical effect on a real person. That is what turns a security headline into an investigation somebody else can follow.

Sources and further reading

References

Numbered sources support the practical points in this article. Video timestamps will appear when the related media is published.

  1. ReportMatt Callaghan / laserphile
    AliExpress webpage keeping multipoint Bluetooth headphones active with WebAudio fingerprintinghttps://blog.laserphile.com/2026/08/aliexpress-webpage-keeping-multipoint.html

    The original investigation that identified two audio contexts, the source script families and the Bluetooth side effect.

  2. ToolTom Ritter
    fireyejs WebAudio fingerprint — standalone reimplementationhttps://ritter.vg/misc/ff/webaudio-alibaba.html

    An independent, no-telemetry reconstruction used to cross-check the deobfuscated Fireye algorithm.

  3. AdvisoryMozilla Bugzilla
    Audio power request prevents sleep on Windows 11 on AliExpress pageshttps://bugzilla.mozilla.org/show_bug.cgi?id=1863193

    The public Firefox defect report, including the observed node graph and oscillator cleanup problem.

  4. ArticleTom Ritter / Mozilla
    WebAudio fingerprinting on Alibabahttps://ritter.vg/blog-webaudio_alibaba.html

    A Firefox engineer’s analysis of WebAudio result diversity after Firefox fingerprinting protections.

  5. ReportChalise and Vadrevu
    A Study of Feasibility and Diversity of Web Audio Fingerprintshttps://arxiv.org/abs/2107.14201

    A 2,093-participant study of WebAudio fingerprint stability, diversity and additive value.

  6. Vendor DocW3C Privacy Working Group
    Mitigating Browser Fingerprinting in Web Specificationshttps://www.w3.org/TR/fingerprinting-guidance/

    Current guidance on active fingerprinting, privacy impact, data minimisation and detectability.

  7. Vendor DocBrave
    Fingerprinting defenses 2.0https://brave.com/privacy-updates/4-fingerprinting-defenses-2.0/

    Brave’s explanation of per-session, per-site randomisation for WebAudio and Canvas outputs.

  8. ArticleTechSpot
    AliExpress was silently running audio in your browser to fingerprint and track your devicehttps://www.techspot.com/news/113581-aliexpress-silently-running-audio-browser-fingerprint-track-device.html

    The news report that prompted this follow-along investigation.

  9. ToolAliExpress Media
    Alibaba AWSC Fireye browser-security bundle, version 1.231.67https://assets.aliexpress-media.com/g/AWSC/fireyejs/1.231.67/fireyejs.js

    The exact live response acquired, hashed and reviewed during this walkthrough.

  10. ToolAliExpress Media
    Alibaba AWSC Collina browser-security bundle, version 1.140.0https://assets.aliexpress-media.com/g/AWSC/uab/1.140.0/collina.js

    The second reported AWSC bundle served during the same isolated acquisition.

  11. Vendor DocVirusTotal / YARA documentation
    Writing YARA ruleshttps://yara.readthedocs.io/en/stable/writingrules.html

    The official guide to YARA strings, conditions and the “of” operator used in this walkthrough.

  12. Vendor DocMDN Web Docs
    AnalyserNode.getFloatFrequencyData()https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/getFloatFrequencyData

    Defines how an analyser copies decibel values for frequency bins into a Float32Array.

  13. Vendor DocMDN Web Docs
    Web Audio API concepts and usagehttps://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API

    An accessible introduction to audio contexts, sources, processing nodes and routing graphs.

  14. Vendor Doccurl project
    curl command-line manualhttps://curl.se/docs/manpage.html

    The authoritative meanings of the curl flags used to extract the selected response.

  15. Vendor DocNIST
    FIPS 180-4 Secure Hash Standardhttps://csrc.nist.gov/pubs/fips/180-4/upd1/final

    The standard defining SHA-256 and the other Secure Hash Algorithms.

  16. Vendor DocGNU Coreutils
    wc invocationhttps://www.gnu.org/software/coreutils/manual/html_node/wc-invocation.html

    The official explanation of wc and its byte-counting -c option.

  17. ToolMozilla Add-ons / Raymond Hill
    uBlock Origin for Firefoxhttps://addons.mozilla.org/en-US/firefox/addon/ublock-origin/

    Mozilla’s recommended listing for the open-source content blocker used for the narrow user-side filters.

  18. Vendor DocuBlock Origin project
    uBlock Origin static filter syntaxhttps://github.com/gorhill/uBlock/wiki/Static-filter-syntax

    The maintained syntax reference for hostname anchors, wildcards, resource types and domain restrictions.

  19. Vendor DocMozilla Support
    Firefox's protection against fingerprintinghttps://support.mozilla.org/en-US/kb/firefox-protection-against-fingerprinting

    Mozilla’s current user guidance for fingerprinting protection and Resist Fingerprinting.

  20. ToolAlibaba CDN
    Alibaba AWSC Fireye browser-security bundle, version 1.234.20https://g.alicdn.com/AWSC/fireyejs/1.234.20/fireyejs.js

    The newer delivery host and Fireye version observed in Firefox during companion-video preflight.

Seen something like this in your own estate?

Find the same weaknesses before an attacker does

Talk to us