Google Ad Injection

This post is getting updates - I’m trying to collect ad samples and investigate with a site owner so I can give away js that site owners can use to detect and block the ads. Please help!

On November 19, Google announced the Google App for iOS is injecting unlabeled ads into pages. They look like the author created them, as seen in Google’s own screenshot:

Google's own screenshot of their ad injected onto the description of a historic monument. It looks a link on the words "Osaka Castle", with no warning that google placed it and no disclosure of being an ad.

Google claims they are injecting ads live now, so I’d like to quickly turnaround a js snippet that sites can use to detect the tampering. I’m strongly reminded of how a motivation for the big lift to HTTPS was slimy ISPs injecting ads into pages.

This system is nearly identical to Microsoft Smart Tags, a 2001 Internet Explorer feature that injected links to Microsoft sites into pages, but without the ability for site owners to disable them with a meta tag. (Recognized by esprehn.)

It’s also similar to Google AutoLink, a 2005 Google Toolbar feature a user could click to inject links into pages.

Google has published no information on how it targets the ads. It may target sites based on Googlebot crawls or may send the url or text of pages that require a login to Google. (Thanks @tasket@infosec.exchange.)

Initial reporting by SERoundtable and 9to5google.

Do you have the iOS Google App installed? Can you find an inserted ad that looks and acts like the screenshot above?

If you have the app and own a site, please check your site. Maybe start with high-ad-value terms like tourist destinations, insurance, loans, attorneys/lawyers, donations, hosting, trading, consumer electronics, or rehab. I would especially appreciate hearing from you so we could iterate on a couple possibilities for detecting this via the DOM.

(I found the victim in their screenshot and have collected 3 negative reports from people unable to reproduce the ad, so it may have been a one-off demo for the announcement.)

“This isn’t ads”

Oddly, a common response has been that the world’s largest advertising company adding links to keywords directed at their own sites isn’t advertising. Sometimes with the caveat that it’s because they’re currently only house ads and not yet for sale to third parties.

A bit of useful history might be Google’s own press release, “Why we sell advertising, not search results”. This was written when AdWords were placed to the side of search results with a blue background to be unambiguously ads. Despite saying that results wouldn’t be for sale, Google slowly iterated the design to make the ads nearly indistinguishable from results.

Today, these injected links start nearly indistinguishable from the author’s own links, with no “Ad” label or icon, and only a faint pastel background similar to the 2010 AdWords treatment. Like AdWords, every experiment Google runs on engagement will show “improvement” as the ads become harder to distinguish,

If you found that an advertising company was adding links to your site without your knowledge or consent, would you consider it a useful service?

Do you think it acceptable that, to disable the injected links, you have to agree to the ad company’s continually-updated terms of service and ask to individually opt-out each of your sites?

Ongoing

There may be more on Bluesky or Mastodon where I’ve tried to find help.

Here’s the first rough draft of a js snippet that a site owner who sees an ad could add to a page to see some more info. It needs text from the link inserted. There’s some assumptions noted for how the injection might appear in the DOM.

(function() {
  const logDiv = document.createElement('div');
  div.style.cssText = 'position: fixed; top: 0; left: 0; right: 0;' +
                      'background: #fee; color: #000; font-family: monospace; ' +
                      'z-index: 999; padding: 10px; max-height: 50vh; overflow: auto;';
  document.body.prepend(div);

  function log(msg) {
    const entry = document.createElement('div');
    entry.textContent = msg;
    logDiv.appendChild(entry);
  }

  try {
    /* edit 'castle' to include the text of an ad link */
    const text = 'osaka castle';

    /* if it's not an 'a', try 'span' and then 'div' */
    for (const ad of document.querySelectorAll('a');) {
      if (a.textContent.toLowerCase().includes(text)) {
        log(ad.outerHTML);
      }
    }
  } catch(e) {
    if (logDiv) {
      log(`Error: ${e.message}`);
    }
  }
})();