Browser fingerprints
A web client’s identity includes the browser it runs in. opentele2-browser generates consistent
browser HTTP headers and full browser fingerprints (screen, navigator, codecs, plugins,
WebGL card, fonts and more), sampled from Bayesian networks the way browserforge samples them. The
same crate draws the web devices of the web API
profiles.
| Python | Node | Rust | |
|---|---|---|---|
| Headers | opentele2. | generate | opentele2_ |
| Fingerprints | opentele2. | generate | opentele2_ |
The Python classes take browserforge’s argument names and defaults, so code written for
browserforge’s HeaderGenerator and FingerprintGenerator keeps working.
Determinism
Every random draw comes from a seedable generator (xoshiro256**), and every set operation keeps
first-seen order. The same seed and options give byte-identical output on any platform, in Rust,
Python and Node. Without a seed every call draws from the operating system’s entropy. A seed is a
string or an integer; an integer counts as its decimal text, so 42 and "42" are the same seed.
from opentele2.browser import HeaderGenerator
headers = HeaderGenerator(browser="chrome", os="windows").generate(seed="my-id")
print(headers["User-Agent"])import { generateHeaders } from 'opentele2'
const headers = await generateHeaders({ browsers: 'chrome', os: 'windows' }, 'my-id')
console.log(headers['User-Agent'])use opentele2_browser::{Browser, HeaderOptions};
fn main() -> opentele2_browser::Result<()> {
let options = HeaderOptions {
browsers: vec![Browser::new("chrome")],
os: vec!["windows".to_owned()],
..HeaderOptions::default()
};
for (name, value) in opentele2_browser::generate_headers(Some("my-id"), &options)?.iter() {
println!("{name}: {value}");
}
Ok(())
}All three give the same headers in the same order, starting with sec-ch-ua and ending with
Accept-Language.
from opentele2.browser import FingerprintGenerator, Screen
fingerprint = FingerprintGenerator(screen=Screen(max_width=1920)).generate(seed=42)
print(fingerprint.navigator.userAgent, fingerprint.screen.width)
print(fingerprint.dumps()[:80]) # JSON, as browserforge's dumpsimport { generateFingerprint } from 'opentele2'
const { navigator, screen } = await generateFingerprint({ screen: { maxWidth: 1920 } }, 42)
console.log(navigator.userAgent, screen.width)use opentele2_browser::{FingerprintOptions, Screen};
fn main() -> opentele2_browser::Result<()> {
let screen = Some(Screen { max_width: Some(1920), ..Screen::default() });
let options = FingerprintOptions { screen, ..FingerprintOptions::default() };
let fingerprint = opentele2_browser::generate_fingerprint(Some("42"), &options)?;
println!("{}", fingerprint.navigator.user_agent);
Ok(())
}Header options
| browserforge / Python | Node | Rust (HeaderOptions) | Default |
|---|---|---|---|
browser | browsers | browsers: Vec<Browser> | chrome, firefox, safari, edge |
os | os | os | windows, macos, linux, android, ios |
device | devices | devices | desktop, mobile |
locale | locales | locales | en-US; at most ten, most preferred first |
http_version | httpVersion | http_version | 2; HTTP/1 falls back to HTTP/2 when no HTTP/1 sample fits |
strict | strict | strict | off: relax the options when nothing fits them |
user_agent (on generate) | userAgent | Header | the User-Agent is one of these |
request_ (on generate) | request | Header | replace a generated header in place, or append it |
In Rust, the overrides go to HeaderGenerator::new(options)?.generate(&mut rng, &overrides), with
Rng::from_seed(seed) as the generator.
A browser can carry bounds on its major version and its own HTTP version: Browser(name, min_version, max_version, http_version) in Python, { name, minVersion, maxVersion, httpVersion }
in Node, opentele2_browser::Browser in Rust. In Python, HeaderGenerator.generate(...) takes the
same options again to override the generator’s for one call.
Fingerprint options
Python (FingerprintGenerator) | Node | Rust (FingerprintOptions) | Meaning |
|---|---|---|---|
screen=Screen(...) | screen: { min | screen: Option<Screen> | inclusive bounds on the screen size, in CSS pixels |
strict | strict | strict | fail instead of dropping a screen constraint nothing meets |
mock_webrtc | mockWebRtc | mock_webrtc | carried to the output as mockWebRTC for an injector |
slim | slim | slim | carried to the output for an injector |
| header keyword arguments | header: { ... } | header: HeaderOptions | the headers the fingerprint is drawn for |
Options nothing can satisfy raise ValueError in Python, reject with OT_INVALID_INPUT in Node
(ERR_INVALID_ARG_VALUE for invalid ones) and return an opentele2_browser::Error in Rust.
Behaviour and data
The crate samples the networks as browserforge does, with seven browserforge defects fixed:
Sec-Fetch values, OS relaxation, the HTTP/1 fallback, Edge detection, the user-agent
constraint, the locale cap and request-dependent headers. In Node the first call parses the
embedded networks, so both functions are async.
opentele2-browser is licensed Apache-2.0. It is a Rust port of
browserforge (Apache-2.0) and embeds the three Bayesian
networks of the Python package apify_fingerprint_datapoints 0.15.0, part of
Apify fingerprint-suite (Apache-2.0). Its
NOTICE credits
both. The rest of opentele2 is MIT.