Rando.gg

How free-game sites hijack your embed without frame-busting

Some game hosts check document.referrer to see who embedded them, and if you are not on their allowlist they set window.location.href to their own homepage. It is not frame-busting: they never touch the parent window, so the browser never blocks it.

We embed games from other people. Most hosts are fine with that. A few ship a script whose only job is to notice they are being embedded and replace themselves with an advert for the site that owns them.

What the code does

The script asks three questions in order. Am I in an iframe? Who put me here? Are they on the list? If the answer to the last one is no, it navigates itself to the host site homepage.

Detecting the frame is trivial: compare parent to window. Identifying the embedder is the interesting part. A cross-origin iframe cannot read the parent URL, so instead it reads document.referrer, which the browser sets to the embedding page. That gets you a hostname without ever touching the parent.

Why it is not frame-busting

Classic frame-busting assigns to top.location, which tries to navigate the parent document. Browsers block that across origins without a user gesture, and have done for years. It mostly does not work any more.

This is subtler and completely reliable. The script assigns to its own window.location, which is always permitted. The parent page keeps working perfectly. It simply stops containing a game and starts containing a storefront.

The frame does not break out. It changes what it is.

The allowlist is the giveaway

The version we found hardcodes twelve permitted hostnames. Eleven are the operator network and its CDNs. The twelfth is sites.google.com, which exists so Google Sites embeds keep working, because that is how a lot of players reach these games from a school network.

11 of 100active games in our catalogue were carrying hijack or ad-injection scripts

Why link checkers never see it

Every automated check we ran called these games healthy, and each was right on its own terms:

  • The URL returns 200, so a status check passes.
  • No X-Frame-Options or frame-ancestors header, so an embeddability check passes.
  • The redirect logic lives in a separate JavaScript file, so scanning the page HTML for suspicious markup finds nothing.
  • It only fires when document.referrer is set to a host that is not on the list. Fetch it without a referrer, the way a crawler does, and it behaves impeccably.

That last point is the one worth internalising. The behaviour is conditional on who is asking. Any check that does not present itself as your site will get the polite answer.

How to find it

Content scan, not status check. Fetch each embed with your own site as the referrer and look for document.referrer appearing anywhere near an assignment to location.href. Also worth flagging: any script whose filename suggests promotion or unblocking, and any page that pulls a video ad tag.

The stronger version is to load the game in a real browser with a real referrer and compare the final URL to the one you asked for. If they differ, you did not get a game.

Common questions

Is embed hijacking the same as frame-busting?
No. Frame-busting tries to navigate the parent window using top.location, which browsers block across origins without a user gesture. Embed hijacking navigates the iframe itself using window.location.href, which is always allowed and never blocked.
Can you stop a hijacking iframe with the sandbox attribute?
Partly. Adding sandbox without allow-top-navigation prevents parent navigation, but this technique does not use parent navigation. Omitting allow-scripts would stop it, and would also stop the game running. In practice the fix is to remove the game, not to contain it.
Why does the allowlist include sites.google.com?
So that embeds on Google Sites keep working. Many players reach these games through a Google Site on a school network, and that traffic is valuable to the host, so it is explicitly permitted while everyone else is redirected.
Does a link checker catch this?
No. The URL returns 200 and sends no framing headers, so a status-based check reports it as healthy indefinitely. Detection requires fetching the page with a referrer and inspecting the JavaScript it loads.

Not feeling this one?

Play something randomOne tap, a game you haven’t played.

More notes