페르소나 파일 형식
페르소나 파일의 JSON 형식으로, 필드와 레이블 규칙, 그리고 함께 정할 수 있는 실행 설정을 다룹니다.
페르소나 파일은 기기의 사실을 적은 JSON입니다. OS, 하드웨어, 화면, GPU, 그리고 읽어 내는 값에 더할 노이즈를 담습니다. bauta는 이를 사이트가 읽을 수 있는 모든 것으로 컴파일하므로 표면끼리 서로 어긋나지 않습니다. 페르소나를 바꾸는 데 다시 빌드할 필요는 없습니다. 파일을 고치고 다시 실행하면 됩니다.
예시
{
"bauta": 1,
"name": "win-intel",
"os": "windows", "os_version": "15.0.0", "architecture": "x86", "bitness": "64",
"hardware": {"cores": 8, "memory_gb": 8},
"screen": {"width": 1536, "height": 864, "avail_width": 1536, "avail_height": 824, "dpr": 1.25},
"gpu": {"vendor": "Google Inc. (Intel)", "renderer": "ANGLE (Intel, ... Direct3D11 vs_5_0 ps_5_0, D3D11)"},
"noise": ["canvas", "audio", "webgl"]
}bauta-browser 저장소의 personas/win-intel.json이 완전한
예시입니다. Intel UHD 630 그래픽을 단 Windows 11 노트북이며, Windows 글꼴 환경 personas/fonts/win.conf와 함께
씁니다.
파일로 페르소나를 만들려면 bauta persona new <name> --from <file>을 실행합니다. 브라우저를 직접 실행할 때는
BAUTA_PLUGIN_CONFIG에 파일의 절대 경로를 지정하며, 브라우저는 stderr에 [bauta] persona: <file>을 출력합니다.
필드
| 필드 | 의미 |
|---|---|
bauta | 1: 이 형식입니다. |
name | 페르소나의 시드입니다. 모든 노이즈가 이 값과 라이선스의 비밀값에서 만들어지므로, 한 이름은 실행할 때마다 똑같이 읽히고, 두 이름이나 두 라이선스는 다르게 읽힙니다. |
os | windows, macos, linux 중 하나입니다. 무엇을 정하는지는 os를 참고하세요. |
os_version, architecture, bitness | 선택 사항입니다. 고엔트로피 Client Hints로, 각각 platformVersion, x86 또는 arm, 64 또는 32입니다. |
brand | 선택 사항입니다. 기본값이자 이 릴리스의 유일한 브랜드인 "chrome"입니다. 다른 Chromium 계열 브랜드("edge", 또는 {"name", "version", "token"})는 거부됩니다. |
hardware | cores(hardwareConcurrency)와 memory_gb(deviceMemory와 Device-Memory 클라이언트 힌트. 데스크톱 Chrome이 보고하는 구간 값인 2, 4, 8, 16, 32 중 하나)입니다. |
screen | CSS 픽셀 단위의 모니터입니다. 키는 screen을 참고하세요. |
gpu | WebGL의 마스킹 해제된 vendor와 renderer입니다. 그래픽 백엔드마다 다른 한계값은 여기에서 따라 나옵니다. |
webgl | 선택 사항입니다. 기기의 WebGL 전체 세트 {"webgl1", "webgl2"}로, 각각 기기를 캡처해 읽은 params, extensions, max_anisotropy를 담습니다. 그러면 페이지는 백엔드가 암시하는 값이 아니라 그 기기의 파라미터와 확장 목록을 읽습니다. |
voices | 선택 사항입니다. 기기의 OS가 가진 음성 합성 목소리 [{"name", "lang"}]로, os가 암시하는 목소리(Windows의 영어(미국) 목소리) 대신 쓰입니다. |
window | 선택 사항입니다. {"frame": [w, h]}는 기기의 브라우저 창이 페이지 둘레에 더하는 크기로, 그 기기의 페이지가 읽는 outerWidth - innerWidth와 outerHeight - innerHeight입니다(기기를 캡처하면 얻을 수 있습니다). 가상 디스플레이에 뜨는 bauta 자신의 창에는 테두리가 없으며, 페이지는 이 값을 읽습니다. |
noise | 결정적 노이즈를 받을 읽기 대상입니다: canvas, audio, webgl. noise를 참고하세요. |
rules | 선택 사항입니다. 사실이 컴파일된 결과보다 먼저 적용되는 레이블 규칙입니다. |
_doc 같은 다른 최상위 키는 무시됩니다. Chrome 버전은 필드가 아닙니다. bauta는 자기 버전을 보고합니다.
os
os는 그 OS의 Chrome이 보고하거나 그리는 대로 다음을 정합니다.
- User-Agent의 플랫폼 부분,
navigator.platform,userAgentData.platform - 페이지가 찾는 API 세트: Windows와 macOS의 Web Share와 Web Bluetooth, macOS의 Barcode Detection API
- 음성 합성 목소리: Windows 페르소나에는 Windows 자체의 목소리
- CSS 시스템 색상과 선택 영역 색상
이 릴리스는 Android 페르소나를 거부합니다.
screen
| 키 | 의미 |
|---|---|
width, height | CSS 픽셀 단위의 모니터입니다. |
avail_width, avail_height | 작업 표시줄이나 메뉴 막대를 뺀 영역입니다. |
dpr | 기기 픽셀 비율입니다. Windows의 125 % 배율로 쓰는 1920x1080 패널이라면 1.25입니다. dpr이 있으면 bauta가 그 배율로 페이지를 그리므로, devicePixelRatio, 화면, 해상도 미디어 쿼리가 서로 맞습니다. |
avail_top | 선택 사항입니다. 사용 가능 영역이 시작되는 위치로, Mac의 메뉴 막대 아래가 그 예입니다. 기본값은 macOS에서는 영역이 남긴 높이 전부, 그 밖에서는 0입니다. |
color_depth | 선택 사항입니다. 24 또는 30입니다. |
color_gamut | 선택 사항입니다. "srgb", "p3", "rec2020" 중 하나입니다. |
hdr | 선택 사항입니다. |
refresh_rate | Hz 단위이며 기본값은 60입니다. 이 머신의 디스플레이와 상관없이 bauta의 프레임은 이보다 빨리 오지 않습니다. |
기본값으로 Mac의 화면은 P3의 30비트, 그 밖의 화면은 sRGB의 24비트이며, HDR은 없습니다.
noise
| 값 | 읽기 대상 |
|---|---|
canvas | getImageData, toDataURL, toBlob |
audio | getChannelData, copyFromChannel. 둘은 같은 샘플을 똑같이 읽습니다 |
webgl | readPixels |
노이즈는 안티앨리어싱된 텍스트와 가장자리, 그라데이션처럼 렌더러가 음영을 넣는 곳에만 들어갑니다. Chrome이 정확하게 그리는 것(채우기, 지우기, 투명 픽셀)은 정확하게 읽히며, 한 픽셀은 캔버스 전체로 읽든, 일부로 읽든, 내보내서 읽든 똑같이 읽힙니다.
거부되는 페르소나
스스로 모순되는 페르소나는 거부되고, bauta가 stderr에 그 이유를 출력합니다
([bauta] persona refused, <file>: <reason>). 그러면 페이지에는 실제 머신이 보입니다. 모순의 예는 다음과 같습니다.
- 다른 OS의 그래픽 백엔드를 이름에 담은 GPU 렌더러(Windows의 Direct3D11, macOS의 Metal)
- 데스크톱 Chrome이 보고하지 않는
deviceMemory - 화면보다 큰 사용 가능 영역
- 다른 Chrome 버전을 말하는 User-Agent 규칙
- 다른 메이저 버전의 Edge
- Android 페르소나
레이블 규칙
레이블 규칙은 WebAPI 호출 하나에, 호출이 끝나는 지점에서 머신 대신 답합니다. 페르소나의 사실이 닿지 않는 곳까지
페르소나를 넓힙니다(rules 필드). 규칙만 담은 파일 {"name": ..., "rules": [...]}도 페르소나이며, 위의 검사 없이
규칙 하나하나로 답합니다.
{"label": "WebGLRenderingContext.getParameter", "arg0": 3379, "return": 16384}| 필드 | 의미 |
|---|---|
label | 답할 호출로, 바인딩 계층이 부르는 이름입니다(레이블 참고). |
arg0 | 선택 사항입니다. 호출의 첫 번째 인자가 이 숫자(예: getParameter 열거값)나 이 문자열(예: getExtension 이름)일 때만 규칙이 맞습니다. |
return | 답입니다. 어떤 JSON 값이든 됩니다. |
perturb | return 대신 써서 호출 자체의 결과에 결정적 노이즈를 더합니다: canvas, audio, readpixels. |
keep | return 대신 씁니다. true면 호출 자체의 결과를 그대로 두므로, arg0가 있는 규칙으로 그 레이블의 arg0 없는 규칙에서 특정 호출을 뺄 수 있습니다. |
레이블
| 호출 | 레이블 |
|---|---|
속성 읽기(예: navigator.platform) | Navigator.get platform |
| 속성 쓰기 | Interface.set name |
메서드 호출(예: gl.getParameter(…)) | WebGLRenderingContext.getParameter |
생성자(예: new AudioContext()) | AudioContext.AudioContext |
인터페이스는 그 멤버를 선언한 WebIDL의 인터페이스입니다. 믹스인의 멤버에는 믹스인을 포함하는 인터페이스의 이름이
붙습니다. navigator.hardwareConcurrency는 Navigator.get hardwareConcurrency이고, 2D 캔버스의
getImageData는 CanvasRenderingContext2D.getImageData입니다. WebGL 1과 WebGL 2는 서로 다른
인터페이스(WebGLRenderingContext, WebGL2RenderingContext)이므로 각각에 규칙을 주세요.
페이지의 워커는 페이지처럼 답합니다. Navigator.* 규칙은 전용, 공유, 서비스 워커의 WorkerNavigator.*에도 답하고,
CanvasRenderingContext2D.* 규칙은 워커의 OffscreenCanvasRenderingContext2D.*에도 답합니다. 워커 자체의
레이블에 대한 규칙이 페이지의 규칙보다 우선합니다.
규칙이 답하는 방식
- 일치.
arg0가 있는 규칙은 호출의 첫 번째 인자가 그 값과 같을 때 우선합니다. 그렇지 않으면 그 레이블에서arg0가 없는 첫 번째 규칙이 적용됩니다. 그래서{"label": "WebGLRenderingContext.getExtension", "return": null}과{"label": "WebGLRenderingContext.getExtension", "arg0": "OES_texture_float", "keep": true}를 함께 쓰면 그 확장 하나만 제공합니다. - User-Agent 계열은 브라우저가 답합니다.
Navigator.get userAgent,NavigatorUAData.get platform,NavigatorUAData.getHighEntropyValues규칙은 페이지와 워커를 가리지 않고 모든 요청의 User-Agent와 Client Hints를 정하며,appVersion은 User-Agent를 따릅니다.userAgent에서는 OS 부분만 쓰입니다. Chrome 버전은 bauta가 직접 쓰므로, 페르소나가 다른 버전을 내세우는 일은 없습니다. - 화면은 그려지는 곳에서 답합니다.
Screen.get width,height,availWidth,availHeight규칙은 화면 자체를 정하므로,screen.*,matchMedia, 스타일시트가 같은 화면을 읽습니다. return. 스칼라, 배열, 객체는 JavaScript 값이 됩니다.- 실제 호출이 Promise를 돌려주면(
userAgentData.getHighEntropyValues,navigator.getBattery등) 답은 이행된 Promise로 돌아갑니다. - 실제 호출이
Int32Array나Float32Array를 돌려주고 규칙이 목록을 주면, 숫자는 그 형식화 배열에 쓰이고 배열은 형식화 배열로 남습니다.MAX_VIEWPORT_DIMS와ALIASED_LINE_WIDTH_RANGE가 그 예입니다. - 속성의 배열이나 객체는 읽을 때마다 같은 객체이며, 배열은 Chrome의
navigator.languages처럼 동결됩니다.
- 실제 호출이 Promise를 돌려주면(
perturb. 결과에 결정적 노이즈를 더합니다. 노이즈는name을 시드로 하며, 이미지나 소리가 눈에 띄게 바뀌지 않을 만큼 작습니다.canvas: 호출이 돌려주는 픽셀(CanvasRenderingContext2D.getImageData), 그리고 캔버스를 내보낸 결과(toDataURL,toBlob)에도 같은 노이즈.audio: 호출이 돌려주는 샘플(AudioBuffer.getChannelData), 그리고 복사해 낸 같은 샘플(AudioBuffer.copyFromChannel).readpixels: WebGLreadPixels의 대상 버퍼(RGBA / UNSIGNED_BYTE 읽기).
실행 설정
게이트웨이를 거칠 때, 페르소나 파일은 브라우저 실행 방식도 정할 수 있습니다. 이 키들은 사실 필드와 나란히 둡니다.
| 키 | 하는 일 |
|---|---|
timezone, locale | 예: "America/New_York", "en-US". 프록시가 있고 둘 다 정하지 않았다면 프록시의 출구 위치를 따릅니다(아래 참고). |
proxy | 예: "http://user:password@host:port", "socks5://user:password@host:port". 사용자 이름이나 비밀번호에 든 :, @, /는 %로 이스케이프합니다. 프록시가 자격 증명을 거부하면 요청은 바로 실패하고, bauta serve가 그렇다고 알려 줍니다. |
humanize | true면 클릭과 타이핑이 사람 손처럼 움직입니다. |
third_party_cookies | "block"이면 Chrome 설정에서 "서드파티 쿠키 차단"을 고른 사람처럼 서드파티 쿠키를 차단합니다. 그렇지 않으면 Chrome의 기본값대로 허용합니다. |
ephemeral | true면 크롤러용처럼 세션 사이에 아무것도 남기지 않는 페르소나가 됩니다. |
requests | 프록시 데이터를 덜 씁니다. preset과 직접 정한 rules를 받습니다(아래 참고). |
{
"timezone": "America/New_York",
"locale": "en-US",
"proxy": "socks5://user:password@host:port",
"humanize": true,
"third_party_cookies": "block",
"requests": {"preset": "balanced"}
}프록시에 맞춘 시간대와 로캘
프록시가 있고 timezone과 locale을 둘 다 정하지 않았다면, 둘은 프록시의 출구 위치를 따릅니다. bauta serve는
프록시를 거쳐 IP 에코 서비스(BAUTA_IP_ECHO, 기본값 https://api.ipify.org)에 묻고, 그 주소의 위치를
bauta geo update가 관리하는 데이터베이스로 찾습니다. 위치를 찾을 수 없고 이전에 찾은 위치도 없다면, 외국 주소 뒤에서
이 머신의 시간대를 보여 주는 대신 페르소나의 브라우저를 시작하지 않습니다.
시간대와 로캘은 바인딩 계층 바깥의 V8과 ICU에서 오므로 실행 설정입니다. 브라우저를 직접 실행할 때는 TZ, 그리고
LANGUAGE와 LANG(예: en_US, en_US.UTF-8)을 설정하세요.
IP 위치 정보는 DB-IP가 CC BY 4.0으로 제공합니다.
requests
requests는 preset("safe", "balanced", "aggressive" 중 하나)과 rules를 받습니다. 브라우저는 HTTP
캐시를 켠 채로 자체 네트워크 계층에서 차단합니다.
| 프리셋 | 차단하는 것 |
|---|---|
safe | 오디오, 비디오, 광고 네트워크의 요청 |
balanced | 여기에 더해 서드파티의 이미지와 글꼴 |
aggressive | 모든 이미지와 글꼴 |
rules는 Chrome의 declarativeNetRequest 형식(block, allow, 요청 헤더의 modifyHeaders)으로 직접 정한 규칙을
더합니다. 프리셋이나 규칙이 무엇을 말하든, 봇 방지 업체의 챌린지와 CAPTCHA는 전부 불러옵니다. 페이지가 이를
통과하려면 그래야 하기 때문입니다. bauta traffic <name>은 페르소나의 페이지가 가져온 것을 종류별로, 그리고 차단된
것과 캐시가 답한 것을 보여 줍니다.
일관된 페르소나 작성하기
핑거프린터는 표면끼리 비교합니다. 위의 사실이 대부분의 표면을 서로 맞춰 주며, 작성자에게 남는 일은 다음과 같습니다.
- 값은 그 등급의 실제 기기가 보고하는 값이어야 합니다. 그 OS의 머신에 실제로 들어가는 GPU, 그에 어울리는 코어 수와 메모리, 흔한 화면 크기를 고르세요.
- 화면은 실행하는 창(
--window-size)보다 작지 않아야 합니다. - 페르소나에 맞는 글꼴 환경(
personas/fonts/)을 함께 쓰세요.
다음 단계
- 페르소나 작동 방식: 페르소나가 무엇이고 어디에 있는지.
- 프록시: 프록시 뒤의 페르소나.
- 대역폭: 프록시 데이터를 덜 쓰는 법.