ChatGPT 광고 수집기, __obi 쿠키로 외부 사이트 활동을 계정에 연결
한 개발자가 자신의 안드로이드폰에서 ChatGPT 광고 수집 동작을 재현해 공개했다. 핵심은 bzr.openai.com이 .openai.com 도메인 범위로 심는 __obi 쿠키다. 이 값은 ChatGPT 로그인 계정과 묶여 있고, 광고주 사이트에 설치된 OpenAI 픽셀을 통해 다시 OpenAI로 전달된다. 즉 사용자가 ChatGPT 밖에서 무엇을 보고 검색하고 구매하는지를 ChatGPT 계정과 이어붙일 수 있는 구조다.
쿠키가 만들어지는 과정은 세 단계다. chatgpt.com 클라이언트가 16바이트 난수를 만들어 /backend-api/bazaar/obi/sync-token(로그아웃 시 /backend-anon/)을 호출하면 백엔드가 RS256 JWT를 돌려준다. 토큰에는 consent_decision: analytics_allowed, consent_policy_version: user_granular_consent_v1, 64자리 헥스 계정 식별자(sub), subject_type: account_user, 22자리 식별자(obi)가 담기고 60초 뒤 만료된다. bzr은 OpenAI가 내부에서 광고 플랫폼을 부르는 이름인 bazaar에서 왔고, 토큰 발급은 wadi 서비스가 맡는다. 이 토큰을 bzr.openai.com/v1/obi/sync로 크로스사이트 POST하면 Domain=.openai.com, HttpOnly, Max-Age=31536000(1년), SameSite=None, Secure 조건의 __obi 쿠키가 내려온다. SameSite=None과 Secure의 조합은 크로스사이트 요청에 쿠키를 실어 보내기 위해 필요한 설정이다.
광고주 페이지에서는 세 종류의 요청이 OpenAI 호스트로 나간다. bzrcdn.openai.com/sdk/oaiq.min.js 로드, obref를 포함한 bzr.openai.com/v1/sdk/events POST, 그리고 자격증명을 생략하는 경로의 같은 엔드포인트 POST다. 마지막 경로는 소용이 없다. 브라우저는 OpenAI 코드가 실행되기 전에 <script src> 요청에 이미 쿠키를 붙이기 때문에, 태그를 로드하는 것만으로 식별자가 노출된다.
SDK는 광고주 페이지에서 신원 정보도 긁어간다. 페이로드는 출처를 in(광고주가 의도적으로 전달), fm(폼 필드), ht(렌더링된 페이지 텍스트), js(태그매니저 버스)로 구분한다. 관측된 트래픽에서 스크래핑된 신원이 광고주 제공 신원보다 많았다(685건 대 255건). 이메일이 가장 많이 나온 곳은 태그매니저 버스로, SDK는 window.dataLayer.push를 자체 함수로 바꾸고 adobeDataLayer도 읽으며 gtm.js 스크립트 태그의 l= 파라미터를 파싱해 이름이 바뀐 GTM 레이어를 찾아낸다. 현재 버전은 이메일과 전화번호를 가져가고, 0.1.31 버전은 8월 27일 범위가 좁혀지기 전까지 이름과 지역 정보까지 수집했다. 이메일·전화번호·이름은 SHA-256으로 해시해 전송하지만 국가·지역·도시·우편번호는 평문으로 나간다. 우편번호가 가장 많이 수집된 폼 필드였다(28개 사이트에서 100건). URL은 origin과 path만 남기고 쿼리스트링은 관측된 23,929건 중 하나도 없었지만, 경로 자체는 그대로 남아 특정 질환, 채무 해결 유입 경로, 소송 상담 폼 같은 정보가 수집기에 도달했다.
규모도 함께 제시됐다. 1,029개 호스트네임에 걸친 936개 광고주 픽셀 트래픽을 수개월간 교차 확인했고, 작성자 기기에서는 __obi 값 하나가 Chewy, Wayfair, ThriftBooks, Eventbrite, HelloFresh, Coursera, SeatGeek 등 12개 상업 사이트에서 13개 픽셀 ID로 전송됐다. 모든 요청은 202로 접수됐다. 30개 __obi 값 중 12개가 둘 이상의 광고주 아래에서 나타났고, 하나는 10곳에 걸쳐 있었다. 로그아웃 상태에서도 동작한다. 해독한 동기화 토큰 932건 중 736건은 account_user, 196건은 anonymous였고, 익명 식별자도 기기당 하나씩 최소 27일간 유지됐다.
배경을 보면 이는 새로운 광고 기술이 아니다. 로그인 계정, 픽셀 발화 시 서드파티 쿠키, 오프사이트 전환의 프로필 결합은 Meta가 수년 전에 만든 구조와 같다. 다만 이를 AI 채팅 제품에 올린 사례는 전례가 없다는 지적이 나온다. OpenAI 쿠키 정책은 __obi를 분석 쿠키로 분류하고 chatgpt.com과 openai.com에 1년 만료로 기재했으며, 해당 섹션에는 이 항목 하나만 들어 있다. OpenAI는 분석과 마케팅 동의를 oai_consent_analytics, oai_consent_marketing으로 분리해 운영하는데, 해독한 모든 동기화 토큰이 analytics_allowed를 담고 있었다. 작성자는 9월 14일 [email protected]과 [email protected]에 __obi를 분석 쿠키로 분류한 이유와, 분석에 동의하고 마케팅을 거부한 사용자에게도 이 쿠키가 전달되는지를 물었지만 OpenAI Support는 내부 검토에 공유하겠다는 답만 보내고 두 질문 모두 답하지 않았다.
개발자 입장에서 실무 영향은 세 갈래다. 첫째, 자사 사이트에 ChatGPT 광고 전환 픽셀을 붙였다면 방문자가 ChatGPT 계정으로 해석될 수 있다는 점을 전제해야 한다. 광고주는 __obi를 읽을 수 없다. 자기 스크립트가 접근할 수 없는 도메인에 속한 값이기 때문이다. 둘째, GTM이나 Adobe 태그매니저를 쓰는 사이트라면 dataLayer 후킹과 폼 필드 스크래핑이 실제로 일어난다는 점을 확인하고, 폼에 들어가는 값의 범위를 다시 점검할 필요가 있다. 셋째, 서드파티 쿠키 차단 정책이 곧 이 메커니즘의 가용성을 좌우한다. 같은 광고주 페이지 요청에서 oai-client-auth-info와 세션 쿠키는 브라우저가 차단했고, SameSite=None으로 설정된 __obi만 살아남았다.
한계도 분명하다. 관측은 안드로이드 Chrome에서 이뤄졌고, Safari의 ITP는 모든 서드파티 쿠키를 막으며 iOS의 Chrome도 WebKit 위에서 돌기 때문에 iOS 브라우저에서는 이 메커니즘이 작동하지 않는다. 데스크톱 Chrome은 검증되지 않았다. ChatGPT 세션 다섯 번 중 한 번꼴로만 동기화 토큰이 발급됐고, 모바일 웹 클라이언트는 동기화 없이 광고를 제공한다. 서버 측에서 이벤트가 계정으로 실제 해석되는 장면은 관측되지 않았다. 202는 쿠키가 붙은 이벤트를 수집기가 받아들였다는 의미이며, 계정 결합은 설계상 따라오는 결론일 뿐 직접 확인된 것은 아니다. 광고주용 쿠키 __obref는 광고주 자체 도메인에 설정되고 사이트마다 값이 달라 다른 사이트를 볼 수 없다(관측된 2,860개 값 중 2,828개가 단일 광고주 아래에만 나타났다).