AI-LECTURER 速報課|2026-09-21|約 25 分鐘

一顆 __obi Cookie 的旅程:拆解 ChatGPT 廣告追蹤如何把別站行為接回你的帳號

取材:ChatGPT now knows what you do on other websites via ad collector(Hacker News(AI 高人氣))
📍 真實場景
林怡君,32 歲,台北一家運動用品電商的行銷專員,負責廣告投放與官網數據

主管想試試在 ChatGPT 上買廣告,請她把 OpenAI 提供的追蹤碼貼到官網。她正準備在週會上報告這件事。

😖 卡住的地方:她只知道 Meta 像素「貼上去就好」。主管追問「訪客的瀏覽行為會被送去哪?會不會連到他們的 ChatGPT 帳號?」時,她答不出來,也看不懂技術文章裡的 cookie、JWT、SameSite。
💡 這課帶她(也帶你)順著一顆 __obi cookie 走完全程,並在瀏覽器裡親手改參數,看它什麼時候會被送出。學完能用工程師的角度回答:資料怎麼流、誰看得到、哪裡有控制點。

🧭 這到底是什麼(白話版)

這則素材是 buchodi.com 作者對 OpenAI 廣告系統所做的技術拆解。重點一句話:OpenAI 的廣告收集器(網域 bzr.openai.com)會在你的瀏覽器裡放一顆叫 __obi 的 cookie網站放在你瀏覽器裡的一小段文字紀錄,之後你每次連到那個網站,瀏覽器會自動把它一起送出去。這顆 cookie 裡的識別碼,是在你使用 ChatGPT 時產生的,並且和你的 ChatGPT 帳號綁在一起。

接著是關鍵:任何在 ChatGPT 買廣告的公司,會在自己的網站裝一小段 OpenAI 的程式碼,就像零售商早就會裝 Meta、Google 的追蹤碼一樣。這種程式碼常被叫做 像素(pixel)貼在網頁裡的一小段追蹤程式,網頁一載入它就會向廣告公司回報「有人來了、做了什麼」。它載入時,會把 __obi 連同你正在瀏覽的頁面資料(搜尋的商品、讀的文章、購買行為)一起送給 OpenAI。作者的結論是:OpenAI 能把你在這些網站上做的事,連到你的 ChatGPT 帳號。

整個機制有三個角色要分清楚。第一個是 subJWT 裡代表「帳號主體」的欄位,文中是一串 64 個十六進位字元,代表「你是誰」。第二個是 obi文中的識別碼欄位,一串隨機產生的 22 字元代號,本身不是姓名,只是拿來「對上」帳號的暗號。第三個是裝著 obi 的 cookie(__obi)。一張短命的 JWTJSON Web Token,一張帶數位簽章的電子票,內容寫著「誰、可以做什麼、到幾點失效」,伺服器能驗證它沒被竄改 把 sub 和 obi 綁在一起,cookie 再把 obi 帶到每個廣告主網站。

先看證據的範圍。作者說他在自己的手機上重現了整個流程,用兩種各自獨立的方式 擷取把手機或電腦送出的網路請求錄下來檢查,俗稱抓包 來驗證,還對照了數個月的觀察流量,涵蓋 936 個不同的廣告主像素、1,029 個主機名稱。這是單一研究者的技術拆解,本課只教文中出現的部分,不替它補上文中沒寫的內容。

🎯 為什麼值得你花時間

行為資料被接上一個登入中的帳號傳統廣告 cookie 認的只是「這台瀏覽器」。文中的 token 卻把 obi 綁在 ChatGPT 帳號(subject_type 為 account_user)上。你在別的網站搜尋的商品、讀的文章、做的購買(文中列出的資料類型),因此有機會對到一個有登入的帳號,而不只是一台匿名的瀏覽器。
使用者幾乎看不見它這顆 cookie 標了 HttpOnly,頁面上的 JavaScript 讀不到;壽命一年(Max-Age=31536000 秒);還被設成可以跨站送出。一般使用者很難察覺,作者也是靠抓包才驗證出來。
不是新招,是新玩家文中提到,零售商本來就會安裝 Meta 與 Google 的追蹤碼,機制本身並不新。新的是接上的帳號來自 AI 聊天服務。對做行銷或做產品的人來說,每次「貼一段第三方程式碼」的決定,都該問:它會把什麼資料送去哪裡?

⚙️ 它是怎麼運作的

1
ChatGPT 產生識別碼並請後端簽章在 chatgpt.com 上,網頁端先產生 16 個隨機位元組,呼叫 POST /backend-api/bazaar/obi/sync-token(沒登入時走 /backend-anon/)。後端回傳一張 RS256用 RSA 私鑰簽名的 JWT 簽章方式,只有握有私鑰的伺服器能簽,任何人都能用公鑰驗證真偽 簽章的 JWT,內容把 sub(帳號)和 obi(識別碼)綁在一起,只給 bzr.openai.com 收,60 秒後過期。文中補充:bzr 是 bazaar 的縮寫,是 OpenAI 內部對廣告平台的稱呼;wadi 是簽發這張票的服務。
2
把識別碼變成 OpenAI 網域上的 cookie網頁端把這張 JWT 以跨站的方式 POST 給 bzr.openai.com/v1/obi/sync。回應帶著 Set-Cookie: __obi,屬性是 Domain=.openai.com、HttpOnly、Max-Age=31536000、Path=/、SameSite=none、Secure。其中 SameSitecookie 的一個屬性,決定「從別的網站發出的請求」能不能附帶它;None 表示都可以 加上 Secure(只走 HTTPS),是 cookie 能在跨站請求中被送出的組合。HttpOnly讓網頁上的 JavaScript 讀不到這顆 cookie,但瀏覽器送請求時仍會自動附上 則是擋 JavaScript 讀取。文中指出,JWT 裡的 obi 與 cookie 的值完全相同。
3
廣告主網站載入 OpenAI 的像素在 ChatGPT 買廣告的公司,會在自己的網站裝一小段 OpenAI 的程式碼,也就是從 bzrcdn.openai.com/sdk/oaiq.min.js 載入的 SDK。網頁一打開,這段程式就開始向 OpenAI 的主機發送請求。
4
瀏覽器自動把 __obi 一起送回 OpenAI文中把從廣告主頁面發往 OpenAI 主機的請求分成三類,在手機的 cookie 罐裡有 __obi 的情況下,三類都帶了它。請求裡同時帶有頁面資料:你搜尋的商品、正在讀的文章、購買行為。請看下方對照表。
5
OpenAI 端把行為接回帳號因為步驟一的 token 已經把 obi 與 sub 綁在一起,OpenAI 收到 obi,就能把這次瀏覽對到帳號。這是作者的結論,也就是「OpenAI 能把行為連到 ChatGPT 帳號」。伺服器內部怎麼儲存這個對應,文中節錄沒有詳述。
文中的三類請求與一個對照組:誰帶了 __obi?
請求有帶 __obi?在做什麼/備註
GET bzrcdn.openai.com/sdk/oaiq.min.js載入像素腳本本身
POST bzr.openai.com/v1/sdk/eventswithobref回報轉換事件(例如有人完成購買)
POST bzr.openai.com/v1/sdk/events(bare body)SDK 的「不帶憑證」路徑。文中說這條路徑沒有擋住 cookie,瀏覽器仍然附上了(節錄在此處中斷,原因請看原文)
GET bzrcdn.openai.com/pixel-config/…沒有(完全沒有 Cookie 標頭)文中當作對照組(control)

🔍 程式碼漫遊(點有 ● 的行看白話講解)

這是 ChatGPT 後端簽發的那張 JWT 的內容(payload)。尖括號 « » 裡的值,是文中刻意遮掉的真實資料。逐行看,就能看出「誰、給誰、做什麼、多久失效」是怎麼被寫死在一張票裡的。

{
"iss": "chatgpt-wadi",
💬 iss 是簽發者。文中說 wadi 是負責簽發這張票的服務。
"aud": "bzr.openai.com",
💬 aud 是受眾:這張票只寫給廣告收集器收。依 JWT 慣例,拿到別的服務就不該被接受。
"purpose": "obi_sync",
💬 用途被限定為「同步 obi」,不是萬用通行證。
"operation": "set",
💬 這次的動作是「設定」cookie。
"consent_decision": "analytics_allowed",
💬 票裡帶著一筆同意決定:分析用途已允許。使用者畫面上怎麼徵求這個同意,文中節錄沒有涵蓋。
"consent_policy_version": "user_granular_consent_v1",
💬 同意政策的版本。從名稱看,是「使用者細項同意」第 1 版。
"sub": "«redacted: 64-hex account subject»",
💬 重點行:sub 就是帳號,一串 64 個十六進位字元。
"subject_type": "account_user",
💬 主體類型是「帳號使用者」,說明這個 sub 指向的是一個帳號。
"obi": "«redacted: 22-char identifier»",
💬 重點行:obi 是 22 字元的識別碼,之後會原封不動變成 __obi cookie 的值。
"exp": "«iat + 60s»"
💬 重點行:到期時間是簽發時間加 60 秒。這張票很短命,但它設下的 cookie 可以活一年。
}

🛠️ 動手做:廣告 Cookie 旅程模擬器:親手改參數,看 __obi 什麼時候會被送出

  1. 先保持預設值(SameSite=None、Secure 已勾選、等待 5 秒),按「① 在 chatgpt.com 取得 __obi」,再按「② 造訪廣告主網站」。你會看到前三類請求都帶了 __obi,第四個對照組沒有。
  2. 把 SameSite 改成 Lax,重按 ① 再按 ②。背景請求全部沒帶 cookie,這就是文中說 SameSite=None 是「跨站送出」必要條件的意思。再改成 Strict 觀察差異。
  3. 改回 None,但取消勾選 Secure,按 ①。看瀏覽器怎麼拒收這顆 cookie。
  4. 把等待秒數拉到 90 秒,按 ①。JWT 超過 60 秒就被拒絕,cookie 根本設不下來。想一想:為什麼票只活 60 秒,cookie 卻活 365 天?答案在下面「工程思維透鏡」。
  5. 注意這是模擬器:它只實作文中提到的 SameSite/Secure 規則與 60 秒過期,頁面資料(例如「跑鞋」)是虛構的,沒有連到任何真實服務。
👇 下面是活的,直接操作

🧠 工程思維透鏡(資深工程師看到的是什麼)

🔭 為什麼要「一張 60 秒的簽章票」加「一顆一年的 cookie」兩段式,而不是直接一次設好?
這是把「授權」和「持續識別」拆開的常見設計。設定 cookie 這個動作橫跨 chatgpt.com 與 bzr.openai.com 兩個網站,伺服器得確認「這個請求真的來自登入中的 ChatGPT」。所以票用 RS256 簽章,aud 只給 bzr.openai.com,purpose 只給 obi_sync,60 秒過期。票就算外洩,能做的事很窄,可用的時間也很短。cookie 則要跨很多次造訪才有價值,所以壽命一年。代價是 cookie 一旦設下,之後一年的識別都靠它,票的短命保護不了後續。以上是根據設計慣例的分析,OpenAI 的實際設計動機文中並未說明。
🔭 SameSite=None; Secure 是設定失誤,還是必然的選擇?
這是必然的。廣告像素的工作就是在別人的網站上向 OpenAI 回報,這種跨站請求要帶 cookie,屬性就只能是 SameSite=None,而且瀏覽器要求同時加上 Secure。你在模擬器裡改成 Lax,就會看到 cookie 不跨站送,整條串接就斷了。所以這個屬性本身不是「壞設定」,是功能所需。該追問的是「誰決定這顆 cookie 該不該被設」。票裡的 consent_decision 與 consent_policy_version 顯示系統有一道同意判斷,但同意畫面長什麼樣、使用者能不能反悔,本課素材沒有涵蓋。工程取捨在於:技術屬性沒辦法同時滿足「跨站可用」與「隱私保守」,保護只能靠同意機制與使用者端的控制。
🔭 這篇拆解的證據可信嗎?資深工程師會怎麼審?
看四件事。第一是可重現:作者在自己的手機上重現了完整機制。第二是交叉驗證:用了兩種各自獨立的擷取方式。第三是規模:數個月的觀察,涵蓋 936 個像素、1,029 個主機名稱,不是單一個案。第四是對照組:pixel-config 那類請求完全沒有 Cookie 標頭,可以看出作者確實區分了「有帶」與「沒帶」。也要看限制:這是單一來源,而且「OpenAI 能把行為連回帳號」講的是能力。文中結論並沒有說 OpenAI 如何使用這些資料,我們也不能替它補上。
🔭 如果你是要貼這段像素的廣告主,該問哪些問題?
至少三個。第一,會送出哪些資料?文中列出商品搜尋、讀的文章、購買行為,你的網站有哪些頁面會觸發這些回報?第二,訪客的同意門檻在哪?像素載入前有沒有先取得同意,你自己的隱私說明對得上嗎?第三,能不能只在需要的頁面才貼?像素是你自己放的,你能決定它出現在哪些頁面,這就是最小化資料的控制點。工程思維的核心是:第三方程式碼進到你的網站,你就要為它送出的資料負起責任。

📝 隨堂考(點選答案,立即回饋)

Q1. __obi cookie 設成 Domain=.openai.com、SameSite=None、Secure,最主要的效果是什麼?
✅ SameSite=None 加 Secure 是 cookie 能在跨站請求中被送出的條件,這正是廣告像素能運作的原因。擋 JavaScript 讀取的是 HttpOnly,不是這個組合。
Q2. 在 JWT 的內容裡,哪個欄位代表帳號、哪個欄位代表識別碼?
✅ sub 是一串 64 個十六進位字元的帳號主體,obi 是 22 字元的識別碼。token 把兩者綁在一起,obi 之後變成 __obi cookie 的值。
Q3. 文中實測的四類請求裡,哪一個完全沒有帶 Cookie 標頭,被作者當成對照組?
✅ 前三類都帶了 __obi,只有 pixel-config 沒有帶,文中把它當作 control。
Q4. 根據這篇文章的節錄,下列哪個結論最站得住腳?
✅ 文中結論是「OpenAI 能連接」,講的是技術上做得到,而且只發生在裝了 OpenAI 像素的廣告主網站。前兩項文中沒有說,第四項與事實不符,cookie 裡放的是識別碼。
Q5. 為什麼常見的設計會讓 JWT 只活 60 秒,而 cookie 卻活一年?
✅ 這是把「授權」和「持續識別」拆開的常見設計理由。OpenAI 的實際動機文中並未說明,這裡屬於工程上的合理推論。

🃏 翻牌記憶卡(先想答案,再點開對答)

__obi 是什麼?點我翻面
OpenAI 廣告收集器在 .openai.com 網域設下的 cookie,值是 22 字元的識別碼,壽命一年(Max-Age=31536000),標了 HttpOnly。
SameSite=None; Secure 代表什麼?點我翻面
允許 cookie 在跨站請求中被附上,而且只走 HTTPS。這是廣告像素能跨站回報的必要條件。
JWT 為什麼只給 60 秒?點我翻面
它只是一次性授權「設定 cookie」的憑證。短命加上 aud、purpose 限定,即使外洩也很難濫用。(設計慣例的推論)
四類請求裡哪個是對照組?點我翻面
GET bzrcdn.openai.com/pixel-config/…,完全沒有 Cookie 標頭。其餘三類(oaiq.min.js、eventswithobref、events bare body)都帶了 __obi。
HttpOnly 擋的是什麼?點我翻面
擋網頁上的 JavaScript 讀取這顆 cookie;但瀏覽器送請求時仍會自動附上它。
作者的證據有多強?點我翻面
自己手機重現、兩種獨立擷取方式、數個月觀察,涵蓋 936 個像素與 1,029 個主機名稱。但這仍是單一來源,且結論是「能連接」,不是「已如何使用」。

✅ 離開前自測(全勾=這課真的學會了)

0%