운영 실측으로 범위가 좁혀졌다: 같은 컨테이너에서 **쿠팡은 60건 정상, 네이버만** 매 IP
첫 요청부터 wtm_captcha. kr_host 도 정상이다(ensure_ports 에 kr.decodo.com, 마커가 해외 IP
하드차단 '비정상적인 접근'(2.6KB)이 아니라 wtm_captcha(43~57KB)). IP·게이트웨이가 아니라
브라우저 지문이 남은 후보다.
**먼저 확인한 것 — 지문 위장은 이 스택에서 불가능하다(중요)**
WebGL 렌더러(컨테이너=SwiftShader)를 실기기 이름으로 덮으려 했으나 주입 경로가 둘 다 막혔다:
context.add_init_script() 무반응. 단순 마커(window.__M__) 주입조차 undefined 다.
patchright 가 무력화한다 — 이 API 는 CDP
addScriptToEvaluateOnNewDocument 로 구현되고 그 흔적이 탐지
신호라, 걷어내는 게 patchright 의 존재 이유다.
확장(MV3, world:MAIN, document_start) 로드는 되지만 값이 바뀌지 않았다.
→ 동작하지 않는 코드를 남기지 않는다. 스푸핑은 넣지 않고, 대신 **판정**만 한다.
재시도 방지를 위해 이 실측을 fingerprint.py docstring 에 남겼다.
**넣은 것**
- services/search/fingerprint.py: 지문 판정(항목·정상여부·관측값·왜 문제인가). 진단이 곧
'무엇을 고칠까'로 이어지도록 이유를 함께 낸다.
- net_meter 토글: 네이버에 계측용 CDP(Network.enable)를 붙이지 않는다(기본 off).
네이버는 '요청 가로채기 자체가 탐지 신호'라 리소스 차단을 끈 이력이 있는데 계측 CDP 는
그대로 붙고 있었다 — 같은 계열이다. [WorkerConfig].naver_net_meter 로 재빌드 없이 A/B
(config 는 마운트). 끄면 대역폭이 DOM 크기 근사로 떨어질 뿐 크롤엔 영향 없다.
쿠팡은 잘 통과하므로 기본값을 바꾸지 않는다.
- Dockerfile.worker: fonts-noto-cjk·fonts-liberation 추가(나눔 하나뿐이면 폰트 지문이 부자연스럽다).
- diag_naver.py 가 같은 판정 모듈을 쓰도록 정리.
통과 환경 기준값(맥 실측): 의심 항목 0개 · HTTP 200 · 6건 · 1.26MB.
테스트 6건 추가(통과 지문을 문제로 몰지 않는지·컨테이너 지문을 놓치지 않는지), 전체 262 passed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
67 lines
2.7 KiB
Python
67 lines
2.7 KiB
Python
"""지문 판정 테스트 — 진단 도구가 '무엇이 문제인지' 정확히 짚는지.
|
|
|
|
이 판정이 틀리면 엉뚱한 걸 고치게 된다. 특히 통과 환경(맥 실측)을 문제로 몰거나,
|
|
컨테이너의 소프트웨어 렌더링을 놓치면 진단 도구의 의미가 없다.
|
|
"""
|
|
|
|
from services.search.fingerprint import is_software_renderer, judge
|
|
|
|
# 2026-08-06 맥에서 실측한 '통과하는' 지문 그대로.
|
|
PASSING = {
|
|
"glRenderer": "ANGLE (Apple, ANGLE Metal Renderer: Apple M3 Pro, Unspecified Version)",
|
|
"webdriver": "false",
|
|
"plugins": 5,
|
|
"korFonts": "Nanum Gothic, Malgun Gothic, Apple SD Gothic Neo",
|
|
"languages": "ko-KR",
|
|
"timeZone": "Asia/Seoul",
|
|
"screen": "2560x1080",
|
|
"avail": "2560x1050",
|
|
}
|
|
# GPU 없는 컨테이너에서 나올 법한 지문.
|
|
CONTAINER = {**PASSING,
|
|
"glRenderer": "ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (LLVM 16.0.0)), SwiftShader driver)",
|
|
"plugins": 0, "korFonts": "(없음)", "screen": "1920x1080", "avail": "1920x1080"}
|
|
|
|
|
|
def _by_name(fp):
|
|
return {name: (ok, value) for name, ok, value, _ in judge(fp)}
|
|
|
|
|
|
def test_passing_fingerprint_has_no_complaints():
|
|
"""통과 환경을 문제로 몰면 진단이 노이즈가 된다."""
|
|
assert all(ok for _, ok, _, _ in judge(PASSING)), _by_name(PASSING)
|
|
|
|
|
|
def test_container_fingerprint_flags_software_renderer():
|
|
r = _by_name(CONTAINER)
|
|
assert r["WebGL 렌더러"][0] is False, "SwiftShader 를 놓치면 진단의 핵심을 놓친다"
|
|
|
|
|
|
def test_container_fingerprint_flags_the_known_gaps():
|
|
r = _by_name(CONTAINER)
|
|
assert r["plugins"][0] is False
|
|
assert r["한글 폰트"][0] is False
|
|
assert r["screen vs avail"][0] is False # 창 관리자 없는 Xvfb
|
|
|
|
|
|
def test_every_finding_explains_why():
|
|
"""이유 없는 경고는 다음 행동으로 이어지지 않는다."""
|
|
for name, ok, value, why in judge(CONTAINER):
|
|
assert why and len(why) > 10, name
|
|
|
|
|
|
def test_software_renderer_detection():
|
|
assert is_software_renderer("SwiftShader driver")
|
|
assert is_software_renderer("Mesa OffScreen")
|
|
assert is_software_renderer("llvmpipe (LLVM 15.0.6, 256 bits)")
|
|
assert not is_software_renderer("ANGLE (Apple, ANGLE Metal Renderer: Apple M3 Pro)")
|
|
assert not is_software_renderer("ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Direct3D11)")
|
|
assert not is_software_renderer("")
|
|
|
|
|
|
def test_non_korean_locale_is_flagged():
|
|
"""한국 IP 로 접근하면서 en-US 면 캡차가 뜬다(실측) — 이 조합을 놓치면 안 된다."""
|
|
r = _by_name({**PASSING, "languages": "en-US", "timeZone": "America/New_York"})
|
|
assert r["languages"][0] is False
|
|
assert r["timeZone"][0] is False
|