DEV Community

RC
RC

Posted on Originally published at randomchaos.us

ChatGPT already knows your other browsing.

OpenAI's ad collector at bzr.openai.com sets a cookie called __obi. It is scoped to .openai.com, its value is written while you are on ChatGPT and tied to your ChatGPT account, and it is then sent back to OpenAI from ordinary websites you visit. Any company that buys ads on ChatGPT installs a piece of OpenAI code on its own site, the same way retailers already run Meta and Google tags. When that code loads, __obi goes to OpenAI along with what you were doing on the page: products you searched for, articles you read, purchase behaviour. OpenAI can resolve all of it to your account.

A researcher reproduced the full mechanism on an Android phone, confirmed it with two independent capture methods, and cross-checked it against several months of traffic covering 936 advertiser pixels across 1,029 hostnames.

The three-step sync

On chatgpt.com the client generates 16 random bytes and calls POST /backend-api/bazaar/obi/sync-token (or /backend-anon/ when you are signed out). The backend returns an RS256 JWT that binds the account sub to the obi identifier, scopes it to the collector with aud: bzr.openai.com, and expires in 60 seconds. The token also carries purpose: obi_sync and consent_decision: analytics_allowed. (bzr is bazaar, OpenAI's internal name for the ads platform; the issuing service is wadi.)

The client then POSTs that token cross-site to bzr.openai.com/v1/obi/sync and gets back:

Set-Cookie: __obi=...; Domain=.openai.com; HttpOnly;
            Max-Age=31536000; Path=/; SameSite=none; Secure
Enter fullscreen mode Exit fullscreen mode

SameSite=None with Secure is exactly the configuration a cookie needs to ride cross-site requests, and Max-Age is one year. The obi value in the JWT and the cookie value are identical. Every other OpenAI cookie observed on advertiser pages was blocked by the browser; __obi is the only OpenAI identifier set with SameSite=None.

From then on, advertiser sites hand it back. On a phone with __obi in the jar, every request class from an advertiser's page to OpenAI's hosts carried the cookie. The pixel SDK has a code path that omits credentials, and it makes no difference: the browser attaches cookies to the <script src> request that loads the SDK, before any OpenAI code runs. Loading the tag discloses the identifier.

What else rides along

The same SDK scrapes identity from the advertiser's page. The payload tags four sources by origin: in for values the advertiser passes deliberately, and fm, ht, js for values pulled from form fields, rendered page text, and the tag-manager bus. Scraped identity outnumbered advertiser-supplied identity 685 events to 255. The SDK replaces window.dataLayer.push with its own function, reads adobeDataLayer, and finds renamed GTM layers by parsing the l= parameter off the gtm.js tag. Current versions take email and phone from the bus; version 0.1.31 also took names and geography until the scope was narrowed on 27 August.

Email, phone and names are SHA-256 hashed before transmission. Country, region, city and postal code go in the clear, and postal code was the most-harvested form field, 100 events across 28 sites. URLs are reduced to origin plus path, and none of 23,929 observed carried a query string, but paths survive, and the paths that reached the collector included a medical condition, a debt-solutions funnel and a litigation intake form. A denylist drops passwords, one-time codes, card numbers, SSN, date of birth, medical history, diagnosis and court fields. Automatic matching was on for 638 of 881 pixels with a known setting, including every credit and lending advertiser in the sample.

On the test device a single __obi value went to OpenAI from 12 commercial sites under 13 pixel IDs, including Chewy, Wayfair, ThriftBooks, Eventbrite, HelloFresh, Coursera and SeatGeek, each accepted with a 202. Across the wider capture, 12 of 30 distinct __obi values showed up under more than one advertiser, one under ten.

It runs when you are logged out

Of 932 decoded sync tokens, 736 were account_user and 196 anonymous, and the anonymous subject is as stable as the account one: a single value per device, persisting at least 27 days.

OpenAI's cookie policy lists __obi under Analytics, one year, and it is the only entry in that section; the policy says analytics cookies help OpenAI understand how its services perform. OpenAI splits consent into oai_consent_analytics and oai_consent_marketing, and every decoded sync token carried analytics_allowed. A user who grants analytics and refuses marketing still gets the cookie and the cross-site resolution.

The researcher put two questions to OpenAI on 14 September: why __obi is classified as analytics, and whether refusing marketing stops it. OpenAI Support acknowledged the inquiry, said it would share the observations internally, and answered neither.

What it means if you run these systems

The mechanism is standard adtech. Meta built the structural equivalent years ago: a logged-in account, third-party cookies on pixel fires, off-site conversions resolved to a profile. Running it on an AI chat product is new, and it matters because people tell these systems things they would never post to a social network, and the systems increasingly act for them.

The behaviour was observed on Chrome for Android. Safari's Intelligent Tracking Prevention blocks all third-party cookies, and Chrome on iOS is WebKit underneath, so no iOS browser runs it; desktop Chrome is untested. Gating is loose: about one ChatGPT session in five produced a sync token, and the mobile web client serves ads without syncing at all, so you may see a pixel fire with no cookie attached. The server-side join was not watched directly; a 202 confirms the collector accepted the event with the cookie attached, and resolution to the account follows from the design.

If you operate one of the advertiser sites, you cannot see any of this. __obi belongs to a domain your scripts cannot read, so you installed a conversion pixel with no way to tell that your visitors are being resolved to a ChatGPT identity. The pixel's other cookie, __obref, is set on your own domain with a different value per site and does not cross sites: of 2,860 values observed, 2,828 appeared under exactly one advertiser.

Top comments (0)