본문으로 건너뛰기

버그 제보

이상한 점을 알려 주시면 고칠게요. 지금 보고 있는 페이지와 브라우저 정보가 함께 담겨요.

페이지:

Frontend

브라우저 지문 채취와 User-Agent 축소

작성 2026. 10. 17.추가
목차11개 항목
출처와 참고

web.dev의 「Learn Privacy」 강좌의 지문 채취 모듈을 바탕으로 재구성한 노트다.

지문 채취란

지문 채취(fingerprinting) 는 기기·브라우저 설정의 오래 변하지 않는 특성을 여러 개 모아, 다른 사용자와 구별되는 조합으로 한 사람을 식별하는 기법이다. 설치된 글꼴 하나도 사람을 가르는 단서가 된다.

쿠키와 다른 점은 보이지 않고 지울 수 없다는 것이다.

구분쿠키지문
사용자가 보고 지울 수 있나있다(설정, DevTools)없다
근거사이트가 심은 값원래 있던 기기 특성
동의배너·설정으로 통제 가능대개 동의 없이 동작

사기 탐지처럼 정당한 용도도 있지만, 서드파티 쿠키가 막힌 뒤 사이트 간 추적의 대안으로 쓰이는 경우가 많다(서드파티 쿠키와 브라우저 추적 방지). 로그인한 사용자를 알아보는 것은 사용자가 동의하고 언제든 로그아웃할 수 있으므로, 지문보다 훨씬 존중하는 식별이다.

수동과 능동

방식정보원브라우저의 대응
수동(passive)요청마다 기본으로 오는 정보: User-Agent, Accept-Language, IP값을 동결·축소한다. 이 값에 기대는 코드는 깨질 수 있다
능동(active)스크립트가 직접 묻는 정보: 글꼴 목록, 화면 크기, 캔버스·WebGL 렌더링 결과, 오디오 처리 결과, 미디어 장치 목록API를 막거나, 권한을 요구하거나, 값을 흐린다

엔트로피

각 특성이 얼마나 사람을 잘 가르는지는 엔트로피(entropy), 비트 단위로 잰다.

  • 값의 종류가 적은 특성(운영체제)은 엔트로피가 낮다.
  • 값의 종류가 많은 특성(설치 글꼴 조합)은 높다.
  • 지문 라이브러리는 여러 특성을 합쳐 하나의 해시로 만든다. 하나하나는 흔해도, 좋아하는 것 열 개를 나열하면 친구 사이에서 한 사람이 특정되는 것과 같다.

미디어 장치는 실제로 쓰지 않아도 장치 이름 목록만으로 정보가 된다. 캔버스에 같은 글자를 그려도 GPU·드라이버·안티에일리어싱 차이로 픽셀이 달라진다.

브라우저의 대응

방법내용예
구현하지 않기정보를 너무 많이 드러내는 기기 API를 아예 만들지 않는다일부 브라우저의 Web Bluetooth·Web NFC 미구현
권한 요구명시적 허락을 받아야 쓴다. 엔트로피는 그대로지만 몰래 쓸 수 없게 된다위치, 카메라, 마이크
값 흐리기결과를 일부러 덜 정확하게 해 모든 사용자를 비슷하게 보이게 한다("군중 속에 숨기")performance.now() 정밀도 낮추기(타이밍 공격 방어도 겸함), 사파리의 단순화된 시스템 정보
동결수동 정보의 세부를 고정값으로 바꾼다UA의 OS·마이너 버전 고정

파이어폭스는 ETP가 알려진 지문 채취 스크립트를 막고, 별도의 지문 보호 모드에서는 더 많은 값을 흐린다.

User-Agent 축소

UA 문자열로 브라우저를 갈라 처리하는 코드가 오래 쌓이면서, 브라우저마다 다른 브라우저인 척하는 문자열이 됐다(첫 토큰 Mozilla/5.0은 의미가 없다). 이제 주요 브라우저는 UA를 동결·축소한다.

남는 정보사라진 정보
브랜드, 주 버전, 플랫폼(대략)마이너·빌드 버전(크롬은 Chrome/<주버전>.0.0.0), 상세 OS 버전(macOS는 10.15.7로 고정), CPU 아키텍처, 기기 모델

UA를 아예 비우면 수많은 사이트가 깨지므로, 지우지 않고 정보를 줄이는 쪽을 택했다.

할 일

  1. 코드와 의존성에서 navigator.userAgent, User-Agent 헤더를 쓰는 곳을 찾는다.
  2. 브라우저 판별을 기능 감지로 바꾼다.
// 나쁨: 브라우저 이름으로 분기
if (/Safari/.test(navigator.userAgent)) { /* ... */ }
 
// 좋음: 기능이 있는지 묻는다
if ('share' in navigator) { /* Web Share 사용 */ }
if (CSS.supports('selector(:has(*))')) { /* ... */ }
  1. 브랜드·주 버전·플랫폼만 쓴다면 대체로 안전하다.
  2. 베타·프리뷰 빌드나 DevTools의 UA 재정의로 축소된 UA에서 시험한다.

User-Agent Client Hints

UA를 대체하는 수단으로 크로미움 계열 브라우저는 Client Hints를 보낸다. 사파리·파이어폭스는 지원하지 않으므로 없을 때를 대비해야 한다.

헤더내용
Sec-CH-UA브랜드와 주 버전 목록
Sec-CH-UA-Mobile모바일 여부(?1/?0)
Sec-CH-UA-Platform운영체제 이름
navigator.userAgentData?.brands
// [{ brand: "Not A(Brand", version: "99" }, { brand: "Chromium", version: "…" }, { brand: "Google Chrome", version: "…" }]
navigator.userAgentData?.mobile // false
  • 기본으로는 이 세 개만 온다. 상세 버전·모델 같은 고엔트로피 값은 서버가 Accept-CH로 요청하거나 JS에서 getHighEntropyValues()로 따로 받아야 한다. 필요한 것만 요청한다.
  • 헤더 값은 문자열이 아니라 구조화 헤더(Structured Headers) 다. 브라우저는 일부러 이상한 가짜 브랜드(Not A(Brand 같은)를 끼워 넣어 엉성한 파싱을 깨뜨린다. 명세에 맞는 파서로 읽고, 목록 순서·특정 문자열을 가정하지 않는다.

사이트가 할 일

서드파티 점검

  • 서드파티 스크립트가 navigator.userAgent를 읽거나 보이지 않는 <canvas>를 만드는지 본다(서드파티와 프라이버시).
  • 공급사 처리방침에서 확률적 매칭(probabilistic matching) 같은 표현을 찾는다. 로그인 ID로 잇는 결정적 매칭(deterministic matching)과 달리, 지문으로 추정해 잇는다는 뜻이다.
  • 내가 필요한 만큼만 모은다(데이터 최소화).

넓게 시험하기

API를 막거나 값을 흐리는 정책은 브라우저마다 다르고, 같은 엔진이라도 API 동작이 다를 수 있다.

범위시험 대상
엔진Blink(크롬·엣지·삼성 인터넷), Gecko(파이어폭스), WebKit(사파리), 데스크톱과 모바일 모두
기기태블릿, 오래된 안드로이드, 업데이트가 늦은 기기(TV·게임기)
빌드Safari Technology Preview, Chrome Canary, Firefox Beta
상태새 프로필, 시크릿·비공개 창, 권한 거부, 파이어폭스 지문 보호 모드

권한을 거부당했을 때 화면이 어떻게 안내하는지는 사람이 본다. 한 번 확인한 뒤의 API 회귀 시험은 Puppeteer의 launch({ args })나 Cypress의 브라우저 실행 옵션으로 브라우저 플래그를 바꿔 자동화한다.

정리

  • 지문은 쿠키와 달리 보이지 않고 지울 수 없어서, 동의 없는 추적에 쓰인다.
  • 엔트로피가 높은 특성일수록 사람을 잘 가르고, 브라우저는 그 값을 막거나 흐리거나 권한 뒤로 숨긴다.
  • UA는 축소됐다. 브라우저 판별은 기능 감지로, 꼭 필요한 정보는 Client Hints로 요청한다.
  • 권한 거부·비공개 모드·여러 엔진에서 시험해 "값이 없거나 부정확할 때"도 동작하게 만든다.

관련 노트: 서드파티 쿠키와 브라우저 추적 방지, 서드파티와 프라이버시, 데이터 최소화와 프라이버시 실천, 웹 보안: CSRF와 XSS

원본 기록2

댓글

GitHub 계정으로 댓글을 남길 수 있어요.