무음 오디오가 블루투스를 끊을 때 — AliExpress의 WebAudio 지문추적

소리 하나 나지 않는데 헤드폰이 폰으로 돌아가지 않는다. 이것은 사용자를 식별하려는 의도적 추적인가, 아니면 지문추적 코드가 잘못 짜인 데서 새어나온 부작용인가 — 그리고 그 구분이 왜 정작 사용자에게는 아무런 위로가 되지 않는가.

도입

한 개발자가 이상한 증상을 쫓다가 웹 추적 산업의 속살을 열어젖혔다. 그의 블루투스 멀티포인트 헤드폰은 원래 PC와 스마트폰에 동시에 연결되어, PC에서 소리가 나면 PC를 우선하고 소리가 멎으면 폰으로 돌아가야 한다. 그런데 어느 순간부터 헤드폰이 폰으로 넘어가지 않았다. PC에서는 아무 소리도 나지 않는데도 오디오 경로가 계속 붙잡혀 있었던 것이다. 범인을 추적한 끝에 그가 도달한 곳은 브라우저에 열어둔 AliExpress 탭이었다.

laserphile 블로그에 올라온 이 조사기(2026년 8월)에 따르면, AliExpress 페이지는 collina.jsfireyejs.js라는 두 개의 난독화된 스크립트를 로드하고, 이 스크립트들이 화면에 보이지 않는 AudioContext 객체를 만들어 브라우저 지문(fingerprint)을 수집한다. 문제는 이 오디오 그래프가 볼륨 0으로 소리 없이 재생되면서도 브라우저와 운영체제(OS)의 오디오 출력 경로를 계속 활성 상태로 붙잡아, 헤드폰이 다른 기기로 전환하지 못하게 만든다는 데 있다. 해커뉴스(Hacker News, item 49372583) 토론은 이 발견을 놓고 “의도된 추적이냐, 조잡한 지문추적의 사고냐”를 두고 갈렸다. 이 글은 먼저 그 메커니즘을 정확히 짚고, 다음으로 이런 은밀한 지문추적이 왜 존재하는지 그 계보와 유인을 파고든 뒤, 마지막으로 사용자 방어와 플랫폼 규제 사이의 트레이드오프를 따진다.

소리 없는 오디오가 기기를 붙잡는 메커니즘

저자가 복원한 오디오 그래프의 구조는 이렇다. 톱니파 오실레이터(Sawtooth oscillator)가 신호를 만들고, 그 신호가 분석 노드(AnalyserNode)를 거쳐 스크립트 처리 노드(ScriptProcessorNode)로 흐르며, 게인 노드(GainNode)에서 볼륨이 0으로 눌린 뒤 최종적으로 AudioContext.destination — 즉 실제 오디오 출력 — 에 연결된다. 핵심은 마지막 연결이다. 저자의 표현을 옮기면, “그래프를 destination에 연결하면 최종 볼륨이 0이더라도 브라우저가 그 그래프를 능동적으로 처리한다(actively process)”. 볼륨이 0이니 사람 귀에는 아무것도 들리지 않지만, 소프트웨어 관점에서는 엄연히 “재생 중인 오디오 스트림”이 존재하는 것이다.

바로 이 지점에서 물리적 부작용이 발생한다. 멀티포인트 헤드폰은 연결된 여러 기기 중 실제로 오디오를 내보내는 기기를 우선한다. PC 브라우저가 볼륨 0짜리 그래프를 쉼 없이 돌리는 동안, OS 입장에서 PC는 계속 “소리를 내고 있는 기기”다. 그래서 PC의 블루투스 오디오 경로가 활성 상태로 유지되고, 헤드폰은 스마트폰으로 되돌아갈 이유를 찾지 못한다. 저자가 지적하듯 브라우저의 탭 음소거(tab mute) 기능도 이 상황에서는 무력하다. 탭 음소거는 <audio><video> 같은 관습적 미디어 요소를 대상으로 동작하는데, 여기에는 그런 요소가 아예 없기 때문이다. WebAudio API로 프로그램이 직접 합성한 소리에는 브라우저가 세워둔 통상적인 제어 장치가 걸리지 않는다.

저자가 이 은밀한 그래프를 어떻게 잡아냈는가도 이 사건의 핵심이다. 그는 페이지 안에서 오디오·비디오 요소나 play() 호출을 찾았지만 단 한 건도 나오지 않았다. 그래서 접근을 바꿔, AudioContext 생성자 자체를 래핑(wrapping)해 컨텍스트가 만들어지는 순간을 기록하고, AudioNode.prototype.connect()를 래핑해 노드가 어디에 연결되는지를 추적했다. 이 계측(instrumentation)으로 미디어 재생 호출은 0건인데도 살아 움직이는 AudioContext가 두 개나 돌고 있음이 드러났다. 즉 눈에 보이는 재생 버튼이나 미디어 태그가 전혀 없는데 소리를 합성하는 파이프라인이 백그라운드에서 상시 가동되고 있었던 것이다.

그렇게 붙잡힌 오디오 그래프가 실제로 수집하는 것은 소리가 아니라 신원이다. 저자에 따르면 이 지문 수집기는 오디오 처리 특성만이 아니라 canvas 렌더링 결과, WebGL 데이터, 화면 크기, 기기 메모리, 브라우저 플러그인 목록, WebRTC 동작, 성능 타이밍(performance timing), 사용자 상호작용 이벤트까지 함께 긁어모은다. 오디오 지문은 이 종합 지문의 한 축일 뿐이다. 해커뉴스 토론에서는 이 부작용이 헤드폰에만 국한되지 않는다는 증언이 이어졌다. 보청기가 블루투스 스트림이 시작될 때 주변 소리를 줄여버린다는 지적, 차량 인포테인먼트가 음성 명령 모드로 오작동한다는 지적, 멀티포인트 연결 전반이 교란된다는 지적이 나왔다(모두 HN 토론 참여자들의 경험담을 의역한 것이다). 소리 없는 코드 한 조각이 사용자의 물리적 기기 동작을 실제로 바꿔놓고 있었던 셈이다.

왜 이런 코드가 존재하는가 — 지문추적의 계보와 은밀성

WebAudio 지문추적은 갑자기 등장한 기법이 아니다. 그 계보는 canvas 지문추적으로 거슬러 올라간다. 같은 그림을 그리라고 명령해도 GPU, 그래픽 드라이버, 폰트 렌더링 엔진, 안티에일리어싱 방식이 기기마다 미세하게 달라, 결과 픽셀에 기기 고유의 편차가 남는다. 오디오 지문추적은 이 발상을 소리 영역으로 옮긴 것이다. 동일한 톱니파를 생성해 처리하게 시키면, 오디오 스택의 부동소수점 연산과 하드웨어·소프트웨어 조합에 따라 미묘하게 다른 출력이 나오고, 그 차이가 브라우저와 기기를 식별하는 안정적 신호가 된다. 쿠키처럼 지울 수 있는 상태를 심지 않고도 기기를 재식별하는, 이른바 무상태(stateless) 식별자다.

여기서 이번 사건의 기술적 아이러니가 드러난다. 해커뉴스에서 여러 참여자가 짚었듯, 오디오 지문추적은 원래 OfflineAudioContext로 구현하는 것이 정석이다. 오프라인 컨텍스트는 실시간보다 빠르게(faster-than-realtime) 계산을 끝내고 스피커로는 아무것도 내보내지 않는다. 지문값만 필요하다면 스피커 경로에 손댈 이유가 전혀 없다는 뜻이다. 그런데 AliExpress의 스크립트는 일반 AudioContext를 쓰고 그것을 실제 출력 destination에 연결했다(HN 토론에서 나온 분석을 의역). 지문을 뽑는 데 필요하지도 않은 실출력 연결이, 바로 멀티포인트를 깨뜨린 원인이다. 그래서 “의도된 방해냐”라는 물음에 많은 논객이 “그보다는 조잡하게 짜인 지문추적 코드의 부작용일 가능성이 크다”고 본 것이다.

의도가 무엇이든, 이런 코드가 번성하는 구조적 유인은 분명하다. 브라우저들이 서드파티 쿠키를 걷어내고 애플의 ITP(Intelligent Tracking Prevention) 같은 방어가 강화될수록, 광고·추적 산업은 쿠키에 기대지 않고도 개인을 이어붙일 무상태 식별자에 목말라한다. 지문추적은 그 수요의 정확한 답이다. 게다가 지문은 광고 타깃팅만이 아니라 부정거래 방지(anti-fraud)·리스크 관리에도 쓰인다 — fireyejs라는 이름 자체가 보안·리스크 컨트롤 계보를 짐작하게 한다. 광고와 보안이라는 두 정당화 논리가 겹치는 지점에서, 상시 지문추적은 “합리적 방어”라는 외피를 얻는다.

가장 문제적인 것은 이 기법의 은밀성이다. 마이크나 카메라에 접근하려면 브라우저가 명시적 권한 프롬프트를 띄우지만, WebAudio로 소리를 합성하는 데는 아무런 권한도 필요 없다(HN 토론에서 반복해 지적된 대목). 게다가 이번처럼 볼륨 0으로 돌리면 브라우저 탭에 스피커 아이콘조차 뜨지 않는다. 사용자가 “이 사이트가 소리를 내고 있다”는 사실을 인지할 창구가 아예 없는 것이다. 여기에 앱 대 웹의 구도가 겹친다. AliExpress는 앱 설치를 집요하게 권하는데, 앱 안에서는 사용자가 추적을 관측하거나 차단할 수단이 웹보다 훨씬 적다. 역설적으로 웹은 애드블록·확장·개발자도구로 이런 코드를 잡아낼 수 있는 그나마 열린 공간이다(HN 토론 의역). 한편 이 특정 지문추적이 Firefox에서는 통하지 않는다는 증언도 있었다. 한 참여자는 Firefox의 기존 방어가 이 지문 수집을 무력화한다며 ritter.vg의 기술 분석을 근거로 들었다(HN 토론 의역). 같은 코드가 브라우저에 따라 다르게 작동한다는 사실은, 방어가 표준이 아니라 개별 브라우저의 정책 선택에 달려 있음을 보여준다.

사용자 방어와 플랫폼 규제의 트레이드오프

그렇다면 사용자는 무엇을 할 수 있는가. 가장 직접적인 처방은 저자가 제시한 것이다. 그는 uBlock Origin 필터 규칙 두 줄로 문제의 두 스크립트를 차단했고, 그렇게 해도 AliExpress 홈페이지가 정상 작동함을 확인했다. 이는 중요한 실측이다 — 지문추적 스크립트를 죽여도 쇼핑 기능 자체는 멀쩡하다는 것은, 그 코드가 사용자 경험을 위한 것이 아니라 사용자를 관측하기 위한 것이라는 방증이다. 더 넓게는 Firefox의 지문추적 저항(resistFingerprinting) 기능, 브라우저 프로파일 격리, 추적이 극심한 사이트는 별도 컨테이너에서 여는 습관이 방어선이 된다. 다만 여기에는 분명한 트레이드오프가 있다. 강한 지문추적 방어나 광범위한 스크립트 차단은 정상 사이트를 깨뜨릴 위험을 동반하며, 대부분의 사용자는 uBlock 필터를 손수 쓰거나 개발자도구로 AudioContext를 계측할 능력도, 의사도 없다. 개인 방어는 소수의 기술 사용자에게만 실효적인 해법이다.

그래서 시선은 플랫폼, 즉 브라우저 자신에게로 향한다. 가장 자연스러운 요구는 “무음 오디오라도 상시 재생 중이면 인디케이터를 띄우거나 권한을 묻자”는 것이다. 하지만 이 처방에도 트레이드오프가 있다. WebAudio는 브라우저 게임, 웹 기반 신시사이저·DAW, 접근성 도구가 정당하게 쓰는 API다. 모든 AudioContext 생성에 권한 프롬프트를 걸면 정상 애플리케이션의 경험이 훼손되고, 프롬프트 피로(prompt fatigue)로 사용자는 결국 아무 생각 없이 허용을 누르게 된다. 더 정교한 접근은 지문추적으로 새어나가는 정보의 총량 자체를 제한하는 것이다 — 오디오·canvas·WebGL 출력에 미세한 노이즈를 섞거나, 한 사이트가 뽑아낼 수 있는 식별 엔트로피에 예산(privacy budget)을 매기는 방식이다. 이는 개별 API를 막는 것보다 근본적이지만, 표준화와 합의에 오랜 시간이 걸리고 브라우저마다 채택 속도가 다르다.

엔지니어 입장에서 이 사건이 주는 교훈은 조금 다른 결이다. 지문추적처럼 은밀하게 설계된 계산조차, 시스템의 다른 층에서 관측 가능한 부작용으로 새어나온다는 점이다. destination 연결 하나가 블루투스 전환이라는 물리 현상으로 번져 결국 정체를 들켰다. 관측 가능성(observability)은 방어자의 무기이자, 조잡한 추적자의 약점이다. 그리고 방어의 성패가 브라우저 정책에 달려 있다는 사실 — Firefox에서는 통하지 않았다는 증언 — 은, 사용자가 어떤 클라이언트를 쓰느냐가 곧 프라이버시 수준을 결정한다는 현실을 다시 확인시킨다.

결론

이것은 의도된 추적인가, 부작용인가. 기술적 증거는 후자 쪽으로 기운다. 지문값만 필요했다면 스피커에 손댈 이유가 없는데, 실출력에 연결한 조잡함이 멀티포인트를 깨뜨렸다. 그러나 사용자의 자리에서 보면 그 구분은 위로가 되지 않는다. 상시 돌아가는 무음 지문추적은 의도했든 아니든 이미 사용자를 식별하고 있었고, 다만 이번에는 헤드폰이 폰으로 넘어가지 않는다는 물리적 증상으로 우연히 정체를 드러냈을 뿐이다. 우리가 감지하지 못하는 수많은 사이트에서, 같은 계산은 아무 부작용 없이 조용히 돌아가고 있을 것이다.

이 사건이 남기는 진짜 질문은 개별 스크립트를 어떻게 차단하느냐가 아니다. 권한 프롬프트도, 마이크·카메라 같은 접근 통제도 없이 브라우저가 임의의 신원 신호를 뽑아내도록 방치된 이 웹의 기본값을, 우리는 언제까지 정상으로 받아들일 것인가다. 지금 당장 할 수 있는 일은 있다 — 추적이 심한 서비스는 앱 대신 웹에서 열고, 애드블록과 브라우저 격리를 켜두고, 프라이버시를 기본값으로 삼는 브라우저를 고르는 것. 하지만 소수의 기술 사용자가 개별적으로 방어에 성공하는 것과, 웹 자체가 사용자를 함부로 지문찍지 못하도록 표준이 바뀌는 것은 전혀 다른 문제다. 당신의 기기는 지금 이 순간, 당신도 모르는 소리를 내고 있지 않다고 확신할 수 있는가.


출처: