~/blog/consent-gate-never-attachedPOSTED
by Shady Nathan Tawfik · October 7, 2026 · 7 min

The Consent Gate That Was Never Attached: A Platform-Wide Tracking Bypass

A four-domain municipal Drupal multisite showed a flawless consent setup on content pages — and none at all on the routes where consent matters most. The banner was not broken: the consent library was never attached to system and legal routes, so analytics and a Meta pixel executed before opt-in on every domain.

Interactive[blog-2.0]

TL;DR: On the homepage the consent setup looked textbook — tracking tags rewritten to text/plain, waiting for opt-in. On /imprint, /privacy, /user/login and an AJAX newsletter route of the same estate, the exact same tags rendered executable: analytics and a Meta pixel fired without consent, a device-storage cookie was set without Secure, and no banner appeared. Four domains, same defect. The lesson: consent software is an attachment problem, and you have to test every route class — not the banner.

The scenario

A municipal marketing organisation runs its city brand on a four-domain Drupal multisite. One platform, one consent manager, one analytics stack. I was asked to look at the web presence end to end, so I started where every visitor starts: the homepage, the legal pages, the login, the forms.

The homepage was reassuring. The consent manager rewrote every tracking tag to an inert template type, the analytics and pixel libraries were loaded only after opt-in, and a fresh browser profile showed zero third-party requests. That is how it should look.

Then I pointed the same fresh-profile test at the legal pages — the pages a visitor opens precisely to understand what happens with their data. On those pages the tracking fired immediately: no banner, no opt-in, device storage written anyway.

I removed all identifying information. The target is described only by sector and technology class so the pattern can be reused.

The pattern

The defect was not a broken banner and not a misconfigured service. The consent library that rewrites tracking tags into opt-in templates was attached to content pages — and not attached to the system and legal routes. On those routes the same tracking modules rendered as plain executable scripts. The visitor gets tracked on exactly the pages where the site explains that tracking would never happen without consent.

The evidence is a route matrix, not a single check

A single check on the homepage proves nothing. The test that mattered was the same probe run against every route class: content, legal pages, login, AJAX endpoints, and the error page. The result was a matrix with one green row (content) and four red ones (system and legal). A fresh browser profile, cache-busting and no interaction are essential — a warm cache or a previous consent decision hides the defect.

The root cause was visible in the HTML

On gated pages the tags carry type="text/plain" and a data-cookieconsent attribute; the consent library's config and preloader appear in the page source. On the affected routes those references were simply absent — zero consent library references in the HTML, while the tracking scripts were emitted as executable <script> elements. The 404 page attached the library again. That makes it a route-dependent attachment bug, not a global misconfiguration: the module behaviour is correct, the attachment scope is wrong.

TL;DR: The homepage looked textbook: tracking tags inert, zero third-party requests before opt-in. On the legal, login and AJAX routes of the same four-domain estate the same tags rendered executable — analytics and a Meta pixel fired without consent, _fbp was set without Secure, and no banner appeared. The gate was never attached on those routes.

Findings

SeverityFindingEvidence
HOCHConsent gate missing on system and legal routes of all four domainsFresh headless profile: analytics + Meta pixel load, _fbp set without Secure, no banner; content pages correctly gated
MITTELTwo domains in the family without SPF at all, one without DMARC/DKIMDNS TXT records; forged mails in the brand name are deliverable
MITTELPublic module blueprint (README, 11 KB) on all four domainsImporters, external API endpoint, plaintext key hint, nightly cron windows
MITTELModule test script anonymously executable on all four domainsHTTP 200 JSON endpoint reachable in production
MITTELAnalytics server three security releases behindVersion disclosed by a public changelog file; XSS-escaping fixes missing
NIEDRIGVersion disclosure, no security.txt, no CAA, installer redirect via plain HTTPHeaders and DNS

The attack path

Consent library attached on content pagesSystem and legal routes excludedTracking tags render executable thereAnalytics and Meta pixel fire before opt-inDevice storage cookie written without SecureData flows to analytics and MetaLegal exposure and tracking of privacy-seeking visitors

Reproducing it in the lab

Every probe below targets a lab multisite I control. None of it is directed at a live system.

~/code/bash bash
# 1. control: does the content page gate its tags and attach the library?
curl -s http://lab.local/ | grep -o 'type="text/plain"[^>]*' | head -3
curl -s http://lab.local/ | grep -c 'consent-preloader'
# <- inert template tags, preloader present

# 2. same check on a system route
curl -s http://lab.local/user/login | grep -c 'consent-preloader'
curl -s http://lab.local/user/login | grep -c 'var _paq'
# <- 0 library references, executable analytics snippet

# 3. the real test: fresh profile, no interaction, per route class
for u in / /imprint /privacy /user/login /ajax/newsletter; do
  TARGET="http://lab.local$u" node consent-audit.js
done
# <- fresh profile on the legal routes: third-party requests + cookie, no banner
~/code/text text
# consent-audit.js asserts, per route:
#   third-party requests before consent == 0
#   cookies before consent              == 0
#   banner visible when tracking exists == true
# any route that violates the first two assertions is a finding.

Detecting it in your own estate

  • Test route classes, not pages: content, legal pages, login, AJAX endpoints, search, error page, print views. Consent libraries attach per route; one excluded template is enough.
  • Use a fresh browser profile with cache disabled for every probe. A previous consent decision or a warm page cache will show green where the visitor sees red.
  • Check the HTML differential: gated tags carry type="text/plain" plus a consent attribute, and the library config appears in the source. If tracking tags sit in executable <script> elements on a route, the gate is missing there — regardless of what the banner says.
  • Assert the outcome, not the tooling: zero third-party requests and zero cookies before opt-in. Make it a CI check over a list of routes and alert on regressions.
  • Keep the privacy policy consistent with reality. A pixel described as "legitimate interest" while the consent config marks it as needConsent is a contradiction a reviewer will find.

Remediation

~/code/text text
# immediately
1. attach the consent library globally (Drupal: hook_page_attachments),
   not via per-route blocks; fix the block visibility that excludes
   system and legal routes
2. remove or gate the AJAX newsletter route; clear all caches
3. re-verify headless, per route class: 0 third-party requests pre-consent
4. block tests/, docs/ and README* under /modules/; rotate the plaintext key

# within one week
5. update the analytics server past the missing security releases
6. unify the email policy: SPF -all, DKIM and DMARC on every domain
7. ship the header baseline on every vhost: HSTS, CSP, X-Frame-Options,
   Referrer-Policy, Permissions-Policy

# within one month
8. add the regression test to CI and to the rollout checklist of
   every consent-tool or template change

Once an attacker gains access, they can perform the following actions:

Track visitors on legal and login pages without consent — device storage before opt-in (TTDSG §25)
Two brands spoofable (no SPF at all): forged giveaway and ticket mails to customers
Public module blueprint: importer endpoints, cron windows, plaintext key hint
Analytics server three security releases behind — XSS-escaping fixes missing
Production test script anonymously executable on all four domains

Takeaways

  • A consent banner is not a consent gate. What matters is whether the tracking tags are inert on every route — and that is an attachment question, not a UI question.
  • The pages users visit to learn about tracking are the worst place for a bypass. Test legal pages first, not last.
  • One green homepage row means nothing. A route matrix with fresh profiles is the only honest test.
  • The same estate rarely has exactly one leftover. A route-scope bug sits next to public module documentation, an open test endpoint and a split email policy.
  • Fix the attachment once, then verify the outcome per route in CI — otherwise the next template change reopens the gate silently.
Shady Nathan Tawfik

Founder & Cyber IT Security Engineer in Bielefeld. Offensive security, audits and full-stack builds. More at

For educational purposes only. Every reproducible command targets my own lab environment (localhost), never a live system. © 2026