feature: шум canvas с seed профиля — отпечатки профилей различаются (#89)
Минимальный init-script (src/api/js/canvas_noise.js): младший бит у ~1/64 непрозрачных пикселей при toDataURL/toBlob/getImageData, детерминирован от seed профиля. Пустой canvas не меняется, подменённые функции неотличимы по toString и length. Старые профили получают seed, экран сохраняется. Проверено: 4 профиля — 4 разных хеша canvas; Cloudflare 6/6 на новых профилях. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4c8394eb4c
commit
9dc2c0b619
6 files changed
+176
-19
No files matched your search
@@ -11,6 +11,7 @@ def test_fingerprint_generated_once(tmp_path):
|
||||
screen = first["fingerprint"]["screen"]
|
||||
assert screen["width"] >= profile.MIN_SCREEN[0] and screen["height"] >= profile.MIN_SCREEN[1]
|
||||
assert first["fingerprint"]["device_scale_factor"] >= 1
|
||||
assert first["fingerprint"]["canvas_seed"] > 0
|
||||
assert "user_agent" not in first
|
||||
|
||||
|
||||
@@ -38,9 +39,33 @@ def test_generate_falls_back_when_browserforge_fails(monkeypatch):
|
||||
raise ValueError("Cannot generate headers")
|
||||
|
||||
monkeypatch.setattr(profile, "FingerprintGenerator", Failing)
|
||||
assert profile.generate_fingerprint(attempts=2) == {"screen": {"width": 1920, "height": 1080}, "device_scale_factor": 1}
|
||||
fp = profile.generate_fingerprint(attempts=2)
|
||||
assert (fp["screen"], fp["device_scale_factor"]) == ({"width": 1920, "height": 1080}, 1)
|
||||
assert fp["canvas_seed"] > 0
|
||||
|
||||
|
||||
def test_browser_kwargs_do_not_touch_user_agent():
|
||||
kwargs = profile.browser_kwargs({"fingerprint": {"screen": {"width": 2560, "height": 1440}, "device_scale_factor": 1.25}})
|
||||
assert kwargs == {"additional_args": {"screen": {"width": 2560, "height": 1440}, "device_scale_factor": 1.25}}
|
||||
def test_browser_kwargs():
|
||||
kwargs = profile.browser_kwargs({"fingerprint": {
|
||||
"screen": {"width": 2560, "height": 1440}, "device_scale_factor": 1.25, "canvas_seed": 12345,
|
||||
}})
|
||||
assert kwargs["additional_args"] == {"screen": {"width": 2560, "height": 1440}, "device_scale_factor": 1.25}
|
||||
assert "useragent" not in kwargs
|
||||
script = kwargs["init_script_content"]
|
||||
assert "const SEED = 12345 | 0;" in script and "__SEED__" not in script
|
||||
|
||||
|
||||
def test_profile_before_canvas_noise_gets_seed_and_keeps_screen(tmp_path):
|
||||
old = {"fingerprint": {"screen": {"width": 2560, "height": 1440}, "device_scale_factor": 2}}
|
||||
(tmp_path / profile.SETTINGS_FILE).write_text(json.dumps(old))
|
||||
fp = profile.load_or_create(tmp_path)["fingerprint"]
|
||||
assert (fp["screen"], fp["device_scale_factor"]) == ({"width": 2560, "height": 1440}, 2)
|
||||
assert fp["canvas_seed"] > 0
|
||||
assert profile.load_or_create(tmp_path)["fingerprint"] == fp
|
||||
|
||||
|
||||
def test_profiles_get_different_canvas_seeds(tmp_path):
|
||||
seeds = set()
|
||||
for i in range(20):
|
||||
(tmp_path / str(i)).mkdir()
|
||||
seeds.add(profile.load_or_create(tmp_path / str(i))["fingerprint"]["canvas_seed"])
|
||||
assert len(seeds) == 20
|
||||
Reference in new issue
Block a user