Is IPRoyal Proxy Manager safe?

Medium risk

IPRoyal Proxy Manager sends a persistent device ID and hashed user ID to Google Analytics on login and other events.

Each time a user logs in, the extension generates or retrieves a persistent UUID stored locally and SHA-1-hashes the numeric user ID, then transmits both to Google Analytics via the Measurement Protocol. A content script injected into every page also writes proxy connection state and WebRTC protection status into a hidden DOM element readable by any page script, which also reveals that the extension is installed.

iproyaldevsv2.2.2Chrome Web Store
45Risk
Who publishes it

IPRoyal Services FZE LLC - no other listings under this identity, 6 shared hostnames

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
iproyaldevs
Declared legal entity
IPRoyal Services FZE LLC
Registered contact
Karolis Toleikis

Shared hosts - 6 hostnames

Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.

apid.iproyal.com
Also called by 1 other listing: IPRoyal Proxy Manager
cdn-apid.iproyal.com
Also called by 1 other listing
dashboard.iproyal.com
Also called by 1 other listing: IPRoyal Proxy Manager
resi-api.iproyal.com
Also called by 1 other listing: IPRoyal Proxy Manager
status.iproyal.com
Also called by 1 other listing: IPRoyal Proxy Manager
iproyal.com
Also called by 2 other listings, including Web Proxy Per Tab

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Login analytics sends persistent IDs to Google Analytics

When you sign in through IPRoyal Proxy Manager, the extension hashes the user ID, combines it with a stored client ID, and posts a login event to Analytics.

No login was tested; no body was captured.

Fields below come from the shipped code.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-359
Source
Dynamic sandbox
What actually happens
You did this

You sign in through the IPRoyal account flow used by the extension.

The content script listens for the page's login event on IPRoyal domains.

The extension did this

The extension prepares a login analytics event with a persistent client ID and a hashed user ID.

The background service worker calls the analytics sender after hashing the numeric user ID.

Fields built into the login analytics request
  • Extension client ID
    fB2qNw7u93YkLJtGQZx0a (illustrative)

    This lets analytics connect multiple extension events from the same browser profile.

  • Hashed account ID
    7c4a8d09ca3762af61e59520943dc26494f8941b (illustrative SHA-1 of a numeric ID)

    This ties the login event to your IPRoyal account without sending the raw numeric ID in this request.

  • Event name
    login

    This records that the event was a sign-in rather than another extension action.

  • Analytics destination
    www.google-analytics.com/mp/collect

    This shows the outside service that receives the event.

Captured request
POSThttps://www.google-analytics.com/mp/collect?measurement_id=G-S5S01SJGJB&api_secret=<redacted>

No response was recorded because the authenticated login path was not exercised during testing.

The code that does this

The login event flows from the page to the analytics sender

Readable version

Content script forwards the page login event

content-scripts/content.js
const h = {  main: () => {    window.addEventListener("iproyal-extension-login2", e => {      l.runtime.sendMessage({        type: "login",        options: e.detail      })    })  },  matches: ["https://*.iproyal.com/*", "https://*.iproyal.dev/*", "http://localhost/*"]};

Background service worker handles login and starts analytics

background.js
const o = async e => {  if (e.type === "login") {    const {      options: t    } = e;    await b.setValue(t.accessToken), r.notifications.create({      type: "basic",      title: v(),      iconUrl: "icon/48.png",      message: C()    }), _(t.userId).then(() => E("login", {}))  }};r.runtime.onMessage.addListener(e => {  const t = e;  typeof t == "object" && o(t)})

Shared chunk builds the Google Analytics request

chunks/_virtual_wxt-plugins-CEAEBqWF.js
const me = async n => {  const e = new TextEncoder,    t = await crypto.subtle.digest("SHA-1", e.encode(n));  return [...new Uint8Array(t)].map(r => r.toString(16).padStart(2, "0")).join("")}, ge = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";let ye = n => crypto.getRandomValues(new Uint8Array(n)),  ve = (n, e, t) => {    let r = (2 << Math.log2(n.length - 1)) - 1,      c = -~(1.6 * r * e / n.length);    return (l = e) => {      let u = "";      for (;;) {        let p = t(c),          d = c | 0;        for (; d--;)          if (u += n[p[d] & r] || "", u.length >= l) return u      }    }  },  Xe = (n, e = 21) => ve(n, e | 0, ye),  Z = (n = 21) => {    let e = "",      t = crypto.getRandomValues(new Uint8Array(n |= 0));    for (; n--;) e += ge[t[n] & 63];    return e  };const we = M.defineItem("local:client-id", {    init: () => Z(),    fallback: ""  }),  ee = M.defineItem("session:analytics-user-id"),  Ze = async n => {    const e = typeof n == "number" ? await me(n.toString()) : void 0;    await ee.setValue(e)  }, et = async (n, e) => {    const t = "G-S5S01SJGJB",      r = "<redacted>",      c = !!r;    if (console.info("[GA]", n, e, c), !c) return;    const [l, u] = await Promise.all([we.getValue(), ee.getValue()]);    await fetch(`https://www.google-analytics.com/mp/collect?measurement_id=${t}&api_secret=${r}`, {      method: "POST",      body: JSON.stringify({        client_id: l,        user_id: u,        events: [{          name: n,          params: e        }]      })    })  }
Destination named by the request-building code
    • www.google-analytics.com

    Receives the Measurement Protocol POST containing the login event, extension client ID, and hashed account ID.

Where it sends data

Destinations our analysis observed IPRoyal Proxy Manager contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.

  • www.google-analytics.comwidely used

    IPRoyal Proxy Manager sends data to www.google-analytics.com. A widely used service: 346 other extensions we have analysed send data here.

Updated 30 September 2026gjakohbhfclfjmhhlenfdkldieofkpjl