Skip to content

Creative sanitization silently blanks script-based creatives; creative iframe sandbox grants allow-same-origin #955

Description

@aram356

Summary

Creative sanitization runs unconditionally on every markup bid. sanitize_creative_html removes script, object, embed, base, meta, form, link, style and noscript together with their inner content. For a script-based creative — the majority of programmatic display — that leaves nothing renderable.

The failure is silent. What usually survives is a tracking pixel, so adm is non-empty, the client renders it "successfully", and the ad server records a normal impression. The slot is simply blank.

Measured impact

From Processed creative debug logs over 291 creative deliveries on a live publisher:

metric value
bytes removed, median 43%
bytes removed, mean 34%
creatives reduced >80% 29 / 291
creatives reduced to <500 bytes 20

Per bidder (median / max loss):

bidder n median max
A 1 100% 100%
B 3 98% 98%
C 43 76% 100%
D 2 63% 100%
E 5 35% 68%
F 220 7% 45%

Bidder C is 43 creatives losing three quarters of their bytes. Bidder A loses everything. All of it without an error.

Why sanitization is load-bearing today

Creative markup renders in an iframe built by createAdIframe, whose sandbox is:

const CREATIVE_SANDBOX_TOKENS = [
  'allow-forms', 'allow-popups', 'allow-popups-to-escape-sandbox',
  'allow-same-origin', 'allow-scripts', 'allow-top-navigation-by-user-activation',
];

allow-same-origin together with allow-scripts removes the sandbox's origin isolation entirely. The codebase already documents this, on ADM_IFRAME_SANDBOX:

Deliberately excludes allow-same-origin: combined with allow-scripts on srcdoc (or first-party src) content, that pair effectively removes the sandbox's origin isolation and would let SSP-provided markup run with the publisher origin's privileges.

APS_RENDERER_SANDBOX omits it for the same reason. CREATIVE_SANDBOX_TOKENS is the only one that grants it, and render.ts states the consequence plainly — "Server-side sanitization is the primary defense against malicious markup; the sandbox provides defense-in-depth."

So sanitization cannot simply be removed: it is currently compensating for a sandbox that provides no boundary.

Note also that the client-side sanitizeCreativeHtml is not a sanitizer — it validates type and emptiness and returns the input unchanged (removedCount is always 0). It provides no backstop.

Proposed change

  1. Add auction.sanitize_creatives so sanitization can be disabled where creatives render in a foreign-origin frame — for example the Prebid Universal Creative inside the ad server's iframe, which rewrite_creative_html already documents as the target context. There the markup cannot reach the publisher origin and stripping it only destroys creatives.
  2. Remove allow-same-origin from CREATIVE_SANDBOX_TOKENS, bringing it in line with its two siblings, so the origin boundary is enforced by isolation rather than by an optional transform.

These must ship together: disabling sanitization while the sandbox still grants allow-same-origin would open both holes at once.

Acceptance criteria

  • Sanitization is configurable and independent of rewrite_creatives
  • Creatives pass through byte-for-byte when disabled
  • The creative iframe no longer combines allow-same-origin with allow-scripts
  • Existing sanitize_* coverage is retained
  • Documented, including when it is safe to disable

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions