🧔🏻‍♂️ gautier.dev

<img
  src="https://example.com/login?next=/favicon.ico"
  onload="alert('Logged in')"
  onerror="alert('Logged out')"
/>

Using images to track third-party logins

TLDR
This is a simple (and old) trick to know if a user is logged in to a third-party website. I read about it a long ago, but I can't find the original article. If you know it, please let me know!

Demo

Here is a list of websites that I can know if you are logged in or not:

  • Google: Loading...
  • LinkedIn: Loading...

It might not work if you have enhanced tracking protection enabled in your browser.

How does it work?

Many websites use an “authwall” for logged out users who try to access a private page, with a ?redirect_to (or similar) parameter to keep track of said page to redirect to it after login. If the user is already logged in, the authwill will be skipped and the user will be redirected to the page directly.

Some vulnerable websites allow arbitrary URLs, and it can be exploited to know if the user is logged in to the third-party website. They usually follow this very naive implementation:

flowchart TD Start(User opens https://example.com/login?next=/favicon.ico) LoggedIn(Redirect to /favicon.ico) LoggedOut(Show a login form) Start -->|"The user is logged in"| LoggedIn Start -->|"The user is logged out"| LoggedOut

The real trick is to use <img />: if the image loads fine, the user is logged in, otherwise the user is logged out.

This cannot work with fetch or XMLHttpRequest because of Cross-Origin Resource Sharing (CORS) security mechanisms, but <img /> is not subject to them.

The implementation is very simple:

<img
  src="https://example.com/login?next=/favicon.ico"
  onload="alert('Logged in')"
  onerror="alert('Logged out')"
/>

It can also be written like this in JavaScript:

const img = new Image();
img.onload = () => alert("Logged in");
img.onerror = () => alert("Logged out");
img.src = "https://example.com/login?next=/favicon.ico";

The URLs used in the demo are:

  • Google: https://accounts.google.com/ServiceLogin?passive=true&continue=https://google.com/favicon.ico
  • LinkedIn: https://www.linkedin.com/authwall?sessionRedirect=/favicon.ico

How to protect against it?

It turns out that being vulnerable to this attack is quite hard, since modern browsers have many security mechanisms to prevent it.

The demo above might not have worked for you because you have privacy features enabled in your browser.

If you develop a website with authentication, here’s what you can do to protect your users:

  • Use SameSite=Strict or Lax on your session cookies (like Github)
  • Only use client-side redirects (like Reddit and Discord)
  • Place the authwall on the restricted page, do not redirect to it (like Twitch)
  • Always show the login page, even if the user is logged in (like Twitter)
  • Do not allow arbitrary post-login pages (like Notion and Wikipedia)
  • Look for a Sec-Fetch-Site: cross-site header to know if the request comes from another website (like Facebook)
  • Always redirect users to the homepage (like dev.to)

I tried a lot of websites to lengthen the demonstration, but I found instead an impressively diverse set of security mechanisms. I’m not even mad, that’s amazing.

Try it yourself

Tinker to see if you can find a vulnerable website:

If you find one, please let me know in the comments below!