程序化广告的供应链天生可被任意插队、冒名。一次曝光从媒体流向买方,中途可能经过多个 SSP、ADX 与转售中介;而 OpenRTB bid request 里描述库存归属的字段——媒体域名 site.domain、App 包名 app.bundle、卖方身份——历史上全由上游自报。自报即可伪造:把 domain 改成 nytimes.com,垃圾流量便能卖出优质库存的价,这是域名伪造(domain spoofing);把未授权库存层层转卖、洗白来源,则是非法转售 / 库存洗钱(inventory laundering)。
IAB Tech Lab 的供应链标准,本质是给这条链逐层盖上可核验的图章:媒体声明谁被授权卖、中介声明每个账号背后是谁、每条请求在运行时记录真实经过的每一跳。这套标准分两类——三份静态声明(ads.txt / app-ads.txt / sellers.json)与一个运行时对象(SupplyChain / schain)。本篇讲透三份静态声明,即「谁被授权卖」;运行时的 schain 与买方交叉核验闭环见下篇《OpenRTB SupplyChain(schain)》。
四件套分两类:三份静态声明(ads.txt/app-ads.txt 由媒体发、sellers.json 由中介发,本篇主角)+ 一个运行时对象(schain 随每条 bid request 逐跳生长,见下篇)。声明回答「谁被授权参与」,运行时回答「谁实际参与了」;买方把「实际」和「被授权」一对,才知道这条路径可不可信。
TL;DR
- 三份声明解决两类欺诈:域名伪造(冒充优质媒体)和非法转售 / 库存洗钱(转卖未授权库存、隐藏真实来源)。核心思路是把「上游自报」升级成「多方对向声明 + 交叉印证」。
- ads.txt:媒体在
https://域名/ads.txt放一份授权卖方清单,每行广告系统域名, 卖方账号ID, DIRECT|RESELLER, 认证机构ID(可选)。DIRECT=直接账户,RESELLER=授权第三方转售。买方抓下来,请求里对不上的卖方即判可疑。 - app-ads.txt:ads.txt 的 App / CTV 版,格式相同。难点是 App 没有 web root——买方用
bundle id反查应用商店里声明的开发者官网(iOS 用 iTunes lookup 的sellerUrl,Android 爬 Play 页面),再到该域名根目录取文件。 - sellers.json:中介(SSP/ADX)在
https://自己域名/sellers.json声明每个 seller_id 背后是谁(name / domain /PUBLISHER | INTERMEDIARY | BOTH,可标is_confidential)。它是 ads.txt 的镜像:ads.txt 从媒体说「谁能卖我」,sellers.json 从中介说「这个账号是谁」,两者互相印证。 - 对账键
(asi, sid):三份声明(以及下篇的 schain)都围着这一对咬合——ads.txt 的「广告系统域名 + 账号 ID」= sellers.json 的「发布域名 + seller_id」。记住它就抓住了主线。 - 核验是尽力而为:文件会更新、app-ads.txt 依赖商店映射、部分 sellers.json 是 confidential;对不完整 / 对不上的路径通常降权而非硬拒,避免误杀真实库存。
- 下篇:schain 把「实际路径」摊开,买方把这三份声明离线抓成索引、用它逐跳回查完成核验闭环,并据真实跳数做 SPO——见《OpenRTB SupplyChain(schain)》。
Table of contents
Open Table of contents
一、问题定义:域名伪造与非法转售
在展开标准之前,先明确它们要防的是什么。OpenRTB bid request 里描述「库存归属」的关键字段有:
site.domain / site.page # Web:媒体域名 / 页面 URL
app.bundle / app.storeurl # App:包名 / 商店链接
publisher.id / publisher.domain
问题在于:这些字段沿途均由上游填写,买方无从验证真伪。由此产生两类典型欺诈:
- 域名伪造(Domain / Bundle Spoofing):低质或作弊流量源把
site.domain伪造成nytimes.com、把app.bundle伪造成某热门 App。买方误以为在买优质库存并出了高价,实际曝光却发生在垃圾位、甚至根本不存在——损害的是买方预算与媒体品牌。 - 非法转售 / 库存洗钱(Inventory Laundering):一份库存被某中介拿到后未经授权层层转卖,经一串 SSP/ADX 把来源洗白,最后以「正规路径」的面目出现在买方面前。买方看不到中间经了几手、每一手是否被授权。
两者的共同根因:供应链上缺少一份「谁被授权代表谁」的可核验记录。IAB 标准正是逐个补上这条记录——先补「声明」(谁被授权,本篇),再补「运行时」(实际走了哪条路,下篇),最后由买方交叉核验把两者对上。
库存洗钱的典型链路。 把「一次曝光机会」视作一批货,洗钱即将来路不明 / 未授权的货倒手数次、洗白来源,再以「正规」名义售出——与洗钱同理,只是标的是库存:
- 作弊 / 未授权流量源 X,实际持有的是垃圾位、机器人流量,或未获纽约时报授权销售
nytimes.com库存; - X 将其转售给 SSP-A,并声称「这是
nytimes.com的库存」; - SSP-A(疏于核查或有意为之)转售给 ADX-B,ADX-B 再转售给 SSP-C,层层倒手;
- 抵达买方(DSP)时,bid request 上
site.domain = nytimes.com,呈现为一条正规、优质的路径。
买方于是以优质库存的价格买到了假货 / 盗卖货。它与域名伪造的区别在于:伪造是直接篡改字段(假冒身份),洗钱是借一串中间商倒手藏匿未授权来源(无证倒卖 + 隐藏路径),后者更为隐蔽。
这个例子贯穿两篇:本篇的三份声明先锁定「谁被授权」(洗钱链中的第一手 X 并不在 nytimes.com/ads.txt 内);下篇的 schain 让「经了几手」可见、由交叉核验逐跳查出「哪一手未授权」——回查即穿帮。
二、ads.txt:媒体的授权卖方清单
ads.txt(Authorized Digital Sellers) 是其中最早、也最简单的一块:媒体在自己域名的根目录放置一个纯文本文件——https://example.com/ads.txt——公开声明**「哪些广告系统、用哪个账号,被授权销售我的库存」**。买方与验证方爬取它,即可判断一条 bid request 声称的卖方是否为媒体所认可。
每一行是一条授权记录,逗号分隔,最多四个字段:
一行 ads.txt 的四个字段:广告系统域名 · 卖方账号 ID · 账户关系 · 认证机构 ID。第①②字段正是后面 schain 里 node 的
asi 和 sid,也是 sellers.json 的对账键——三份声明在这里第一次咬合。
四个字段:
| 字段 | 含义 | 说明 |
|---|---|---|
| 广告系统域名 | 被授权的 SSP/ADX 域名 | 对应 schain node 的 asi、sellers.json 的发布域名 |
| 卖方账号 ID | 媒体在该系统里的账号 | 对应 schain node 的 sid、sellers.json 的 seller_id |
| 关系类型 | DIRECT 或 RESELLER | DIRECT=直接账户;RESELLER=授权第三方代卖 |
| 认证机构 ID | TAG-ID(可选) | 用于进一步确认广告系统身份 |
前两个字段——asi 和 sid——是贯穿整套标准的「对账键」,此处先明确(sellers.json 与下篇 schain 会反复用到):
asi(Advertising System Identifier,广告系统标识):一个中介(SSP/ADX)的域名,如google.com、openx.com,回答「这一跳由哪个广告系统销售」。按约定它即为该系统 sellers.json 所在的域名,因此拿到asi即可拼出https://{asi}/sellers.json查询。sid(Seller ID,卖方账号 ID):媒体在某广告系统内的账号编号,如pub-0000…、540123,回答「在该系统内这是谁的账号」。同一媒体在不同系统里sid各异;sid仅在其所属asi下才有意义。
因此单独的 sid 无意义,必须成对使用:(asi, sid) = 「在 asi 这个广告系统内、编号为 sid 的卖方账号」,在全网范围内唯一锁定一条「谁通过谁销售」的授权关系。它在多份声明中处处一致,才算对得上账:
ads.txt 一行 : asi = 广告系统域名, sid = 卖方账号ID
sellers.json 一条: asi = 发布该文件的中介域名, sid = seller_id
schain 一个 node : asi = node.asi, sid = node.sid (见下篇)
DIRECT 与 RESELLER 是理解供应链形状的关键:
- DIRECT:媒体与该广告系统存在直接账户关系,结算直接支付给媒体,是最短、最干净的路径。
- RESELLER:媒体授权第三方代为转售其库存,对应供应链下游多出的一跳;它合法,但买方通常更偏好 DIRECT 路径(更短、抽成更少)。
以一条真实的 DIRECT 记录为例,将四个字段落到业务含义上:
google.com, pub-3469257745550756, DIRECT, f08c47fec0942fa0
- 媒体为变现接入 Google 的广告系统(App 场景常为 AdMob 或 Google Ad Manager)——即 asi
google.com; - 媒体在该系统内申请到代表自身的发布商账号
pub-3469257745550756——即 sid; DIRECT表示媒体与 Google 为直接账户关系,由 Google 直接与媒体结算。
一个常见误解需澄清:Google 在这条链里是「销售库存的交易所」,而非「填充广告的广告主」。媒体将库存授权 google.com 销售,Google 发起拍卖、撮合众多广告主/买方竞价,由出价最高者的广告填充该展示位;Google 扣除技术费后将余款直接结算给媒体。即:sid 回答「这是谁的账号」,asi 回答「谁在代其销售」,DIRECT/RESELLER 回答「隔了几层」——填充进来的广告来自拍卖胜出的需求方,与 asi 无关。
第④字段的认证机构 ID(上例中的 f08c47fec0942fa0)需单独说明:它是广告系统(asi)在第三方认证机构登记的全球唯一身份编号,实践中该机构基本仅有一家——TAG(Trustworthy Accountability Group),故它几乎总是一个 TAG-ID。要点有三:
- 它绑定 asi(广告系统),而非 sid(账号):整份文件中凡
google.com的记录,第④字段都应是同一个f08c47fec0942fa0;pubmatic.com的记录都应是5d62403b186f2ace。前三字段回答「谁被授权销售」,这一字段再加一层「该 asi 确为那个广告系统」的身份背书——asi 域名是「自报的名字」,TAG-ID 是「第三方签发的身份证号」,用以防范相似域名冒充大厂。 - 它是可选的:许多记录仅有三字段(如
smartadserver.com, 3117, RESELLER),完全合法。 - 它在现实中错误率很高:真实文件常见 TAG-ID 被写错、截断、混入字母(同一 asi 在不同记录给出不一致的 TAG-ID)。因此成熟买方核验的主键始终是
(asi, sid),TAG-ID 仅作辅助确认,绝不因其对不上就将流量判为欺诈——否则会大量误杀。
ads.txt 还支持几个变量行,用于更复杂的所有权场景:
CONTACT=ops@example.com # 联系方式
SUBDOMAIN=news.example.com # 指向子域名自己的 ads.txt
OWNERDOMAIN=example.com # 声明真正的所有者域名
MANAGERDOMAIN=ssp.com # 声明代运营变现的一方
一句话本质:ads.txt 把「谁能销售我的库存」从媒体的私下约定,变成一份任何人都能抓取、核对的公开清单。买方的用法极简:请求中声称的卖方
(asi, sid)不在清单内 → 判定为伪造或未授权。伪造者可篡改 bid request 中的字段,却改不了nytimes.com根目录下那份由纽约时报自身控制的 ads.txt——信任锚点从「上游自报」转移到「媒体自身掌控的公开文件」,这正是整套设计的支点。
三、app-ads.txt:面向 App / CTV 的同一机制
ads.txt 依赖「域名根目录」这个天然锚点工作,但 App 与 CTV/OTT 没有 web root——com.foo.bar 这样的 bundle id 并非可拼接 /ads.txt 的地址。app-ads.txt 解决的正是这一定位问题,文件格式与 ads.txt 完全一致,唯一差别在于如何找到该文件:
- 从 bid request 拿到
app.bundle(包名)或app.storeurl; - 用它去应用商店(App Store / Google Play)查到这个 App 的 listing;
- 从 listing 里读出开发者声明的官网域名(developer website);
- 到该域名根目录取
https://developer.com/app-ads.txt。
即:App 的授权信任锚点,转移到了「开发者在商店中声明的官网」——因为商店 listing 是开发者身份的可信来源,无法冒充。此处的 listing 即在商店中打开某个 App 所见的详情页(图标、截图、评分,以及关键的**「开发者网站 / Developer Website」字段**);开发者上架时须通过商店审核并登记该官网,攻击者无法篡改——这正是「信任锚点」的由来。
「从 listing 读出开发者域名」的具体做法因平台而异:
- iOS(规整):Apple 提供官方接口,可用包名或数字 track id 直接查询——
https://itunes.apple.com/lookup?bundleId=com.foo.bar(数字 id 则用?id=),返回 JSON 中的sellerUrl即开发者官网,无需爬取页面。 - Android(杂乱):Google Play 无对应官方 API,只能抓取
https://play.google.com/store/apps/details?id=包名的 HTML,从「开发者信息」区块中解析出 Website 链接的href。
取得官网链接后还须归一化为根域名再拼接路径:去除 www、路径与 query,按 public suffix list 取 eTLD+1(https://www.developer.com/x?utm=.. → developer.com),最终取 https://developer.com/app-ads.txt。
这条链看似直白,工程上却是这套标准中最棘手的一环:bundle id → 开发者域名 的映射需持续爬取各大应用商店并解析页面,而商店结构会变动、存在反爬与地区差异;一旦 App 更换开发者、或 bundle 被仿冒,映射便可能出错。多数买方或自行维护这张映射表,或采购第三方数据。
一个反常识但重要的判断:许多 App 并无 app-ads.txt,且属正常。app-ads.txt 仅对「将库存投放开放程序化市场、经第三方 SSP 转售」的媒体才有意义。新闻聚合类 App(挂载数十个 SSP 变现)会有一份上千行的 app-ads.txt;而订阅 / 内购型(如 Discord)、围墙花园(如 TikTok——广告仅在自家闭环买卖、不走 OpenRTB)、纯工具类(如各类卖家后台)均不参与开放供应链,自然不发布该文件。因此「查不到 app-ads.txt」通常意味着该 App 不做开放程序化,而非欺诈——核验时应归为「降权 / 不适用」而非「拒绝」。反之,若一条 bid request 声称是某围墙花园 App 的库存、却带着一串外部 SSP 的 schain,才是应予警惕的信号。
CTV 场景更复杂:设备、渠道、App 形态碎片化,
app.bundle口径各家不一,app-ads.txt 的覆盖率与准确率均低于 Web。这也是 CTV 至今仍为欺诈重灾区的原因之一。
四、sellers.json:中介的卖方声明(ads.txt 的镜像)
ads.txt/app-ads.txt 是从媒体出发的声明——「谁能卖我」。但买方收到 bid request 时,直接对话的是中介(SSP/ADX),请求携带的是「某广告系统的某个 seller_id」。买方还需要反方向的一份声明:这个 seller_id 究竟是谁? 这便是 sellers.json。
每个中介在自身域名根目录发布 https://ssp.com/sellers.json,逐条声明其系统内每个账号背后的实体:
{
"sellers": [
{
"seller_id": "540123",
"name": "The New York Times",
"domain": "nytimes.com",
"seller_type": "PUBLISHER"
},
{
"seller_id": "889001",
"name": "SomeReseller Inc",
"domain": "somereseller.com",
"seller_type": "INTERMEDIARY"
}
]
}
一条 seller entry 即中介为「系统内某个账号」签发的一张身份卡片,四个核心字段各回答一个不同的问题:
seller_id——「哪个账号」(对账主键):该卖方在此中介系统内的唯一账号 ID,正是 ads.txt 的「卖方账号 ID」、schain node 的sid。它是个不透明 token(可能是540123、pub-3469…,也可能是一串随机 ID 如cki4jtpu279410oa5z25dfux6),本身不含信息——必须作为 key 查表,是将(asi, sid)在多份文件间对齐的那把钥匙。name——「叫什么名」(供人阅读的标签):账号背后实体的公司名(如The New York Times)。仅便于人工核对与报告,机器核验不依赖它——名称可任意填写、且可能重名。domain——「身份是谁」(回查锚点,最关键):账号对应实体的域名。这是核验的接力棒——买方据此回查domain/ads.txt(app-ads.txt)是否授权了(asi, sid)。domain质量直接决定这一跳是否可信:强域名(nytimes.com)可干净回查;免费 / 代托管域(*.blogspot.com、*.firebaseapp.com、*.app-ads-txt.com)身份绑定弱、可信度低;空domain则无从回查。seller_type——「终点还是中间」:PUBLISHER(账号为媒体本身,这一跳应是链的终点,回查其 app-ads.txt 即可)/INTERMEDIARY(为转售中介,这一跳并非终点,schain 中理应还有更上游的 node)/BOTH。它帮助买方判断「链在此是否走完」——PUBLISHER之后仍有跳、或INTERMEDIARY却在此断链,均为可疑信号。
另有两个可选标记:is_confidential(允许中介出于商业保密隐藏 name/domain,代价是该路径无法完全核验、只能降权)、is_passthrough(标记该节点仅为透传、并非真正的卖方账户)。
顶层还携带中介自身的身份信息:contact_email、version,以及 identifiers(常含该中介的 TAG-ID,可与 ads.txt 第④字段相互印证;有时还含 DUNS 企业编码)。
如何获取:sellers.json 的位置完全由 asi 决定——
https://{asi}/sellers.json,asi 为何即据此拼接,无需像 app-ads.txt 那样翻译域名。买方从抓取到的所有 ads.txt 中收集全部 asi,逐个拉取整份 sellers.json 备用。(买方如何把三份声明抓全、清洗、编译成可在竞价热路径上查询的索引,见下篇的工程章节。)
ads.txt 与 sellers.json 是方向相反、必须互相印证的一对:
同一个
540123,在媒体的 ads.txt 里被声明为「openx 可 DIRECT 卖我」,在 openx 的 sellers.json 里被声明为「就是 nytimes.com 这个 PUBLISHER」。两个方向对上,卖方身份才成立;若 sellers.json 里 540123 指向的是别的域名,或 ads.txt 里根本没这条,就是可疑路径。
一句话本质:ads.txt 是媒体侧的「谁能卖我」,sellers.json 是中介侧的「这个账号是谁」。单份声明易于造假,两份对向声明互相印证则难——这正是从「信任」走向「可核验」的核心手法。而把「实际经过了哪些中介」这条运行时路径接入、完成完整核验闭环,则须依靠下篇的 schain。
五、常见误区 ↔ 正解
| 常见误区 | 正解 |
|---|---|
| ads.txt 能「阻止」欺诈 | 它只提供可核验的授权清单;真正的拦截靠买方核验并拒绝。媒体部署了、买方不核验,等于无效 |
| ads.txt 和 sellers.json 是一回事 | 方向相反:ads.txt 媒体侧「谁能卖我」,sellers.json 中介侧「这个账号是谁」,二者对账才成立 |
| app-ads.txt 就是把 ads.txt 换个名 | 格式相同,但定位机制不同——靠 bundle→商店→开发者域名 反查,这层映射是工程难点 |
| RESELLER 路径都是可疑的 | RESELLER 是合法授权的转售;可疑的是「对不上任何授权」的路径。只是买方通常更偏好更短的 DIRECT |
| App 没有 app-ads.txt 就有问题 | 未必。订阅制 / 围墙花园(TikTok)/ 工具类本就不走开放程序化,没有很正常;只对参与开放转售的媒体才有意义 |
| 认证机构 ID(TAG-ID)是硬核验键 | 它绑 asi、可选、且现实中抄错率高;核验主键是 (asi, sid),TAG-ID 只做辅助,不因它对不上而硬拒 |
| 部署了标准就 100% 干净 | 覆盖不全、confidential、CTV 口径混乱、映射滞后——核验永远是尽力而为,用降权兜住灰色地带 |
程序化供应链的这三份声明,看似只是「两份文本文件加一个 JSON」,本质却是一次把信任换成核验的系统改造:媒体以 ads.txt/app-ads.txt 掌控「谁能代表我」,中介以 sellers.json 公开「系统内每个账号是谁」,买方将两个方向相互印证,域名伪造与非法转售中授权这一半便无处藏身。理解了那个反复出现的对账键 (asi, sid) 如何在两份声明中咬合,再进一步看 schain 如何把「实际路径」接入,便理解了整个 AdTech 透明度运动的缩影——将每一个「信任来的」环节,逐一换成「可核验的」环节。
延伸阅读
- OpenRTB SupplyChain(schain):运行时逐跳记录与核验闭环:本篇的下篇——三份声明「谁被授权」,schain 记「实际经过谁」,买方把两者一对完成核验,并据跳数做 SPO。
- 程序化广告生态全景:本篇的母篇——第 7 节的 ad tech tax 与 15% unknown delta,正是供应链透明度要解决的问题。
- Last Look 深挖:透明度运动的另一半——拍卖侧从「偷看」到「一价 + 可审计日志」,与供应链侧同一个母题。
- 程序化交易模式:RTB / PMP / PD / PG:不同交易模式下供应链的长短与可控性差别。
- 媒体 Ad Server 深挖:媒体侧库存分配与授权关系(DIRECT / RESELLER)从哪来。
- Bidder 请求解析:schain / domain / bundle 在 bid request 里怎么被解析出来。
- Bidder 频次控制工程篇:供应链核验「离线抓取建索引 + 热路径本地缓存」的同款工程打法(详见下篇)。
- IAB TCF 与同意管理:另一套贯穿全链路的 IAB 标准,隐私合规侧的「可核验」。
一手资料与规范:
- IAB Tech Lab. ads.txt & app-ads.txt Specification:授权卖方清单的字段定义与 app-ads.txt 的开发者域名解析规则。
- IAB Tech Lab. sellers.json Specification:卖方身份声明的字段与保密(confidential)机制。
- IAB Tech Lab. OpenRTB 2.6 Specification:
site.domain、app.bundle、publisher等字段的权威定义。 - ISBA & PwC. Programmatic Supply Chain Transparency Study:unknown delta 与「日志对不上账」的实证来源。