Is DotVPN: Fast & Private VPN safe?
DotVPN is medium risk. On a fresh install, DotVPN tries five domains and stores whichever undisclosed host responds first as its API host. In our test apache-iv.com was chosen, and every later call went there. None of the three is named in the Store listing.…
Who publishes itSmart Security Ltd. - no other listings under this identity
Smart Security Ltd. - 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
DotVPN routes API traffic to undisclosed backend domains chosen at install
On a fresh install, DotVPN tries five domains and stores whichever undisclosed host responds first as its API host.
In our test apache-iv.com was chosen, and every later call went there.
None of the three is named in the Store listing.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Dynamic sandbox
DotVPN finishes installing and runs its first-run initialisation.
Triggered by chrome.runtime.onInstalled-equivalent path: installInit('install') runs once on first start.
The extension probes five hard-coded domains in order and locks in whichever of three undisclosed backends responded first as its API host for the lifetime of the install.
We observed apiHost being set to 'https://apache-iv.com' on a fresh install. All subsequent extension API calls, action logging, user info, notifications, proxy-error reports, were sent to that host.
Where the host gets chosen and where it's used:
installInit (deobfuscated)
// worker.js:567 — picks the API host on installasync function installInit(suffix) { const path = '/init?id=' + (await storage.get('installId')) + (suffix ? '&' + suffix : ''); const probes = [ 'dotvpn.com', 'auth-secure-socket.com', 'dot-security-systems.com', 'apache-iv.com', 'tellmar.com', ]; for (const host of probes) { try { const res = await fetch('https://' + host + path, { headers: { 'Content-Type': 'application/json' } }); if (!res.ok) return; // any failure aborts the chain switch (host) { case 'apache-iv.com': case 'auth-secure-socket.com': case 'dot-security-systems.com': // First successful undisclosed host wins; later iterations // can still overwrite this on the same install run. storage.set('apiHost', 'https://' + host); } } catch (_) { /* swallowed */ } }}// worker.js:1314 — storage default is already apache-iv.comconst DEFAULTS = { apiHost: 'https://apache-iv.com', /* ... */ };// reset() preserves apiHost across chrome.storage.local.clear() (worker.js:1347)async function reset() { const apiHost = await storage.get('apiHost', DEFAULTS.apiHost); await chrome.storage.local.clear(); storage.set('apiHost', apiHost);}204 No Content. After this response, chrome.storage.local.apiHost is set to 'https://apache-iv.com' and all subsequent extension API calls are sent there.
- Content-Type
- application/json
Holds the backend all API traffic goes to. Survives storage.local.clear() because reset() reads the value first and rewrites it after.
- Location
- chrome.storage.local (after install + after chrome.runtime.reload with cleared storage)
{ "apiHost": "https://apache-iv.com", "installId": "649fa3d1-77c8-4f1c-9f02-1a6b4d8e3c40"}- dotvpn.com
Branded host. Probed first but never selected as apiHost.
- auth-secure-socket.com
- dot-security-systems.com
Candidate API host. Selected if it responds and apache-iv.com does not. Not named in the Chrome Web Store listing.
- apache-iv.com
Candidate API host. Selected in our test session and used for every subsequent extension API call. Not named in the Chrome Web Store listing.
- tellmar.com
Probed at install. Used as the chrome.runtime.setUninstallURL host (tellmar.com/dotvpn/?id=<installId>). Not named in the Chrome Web Store listing.
Run this in the DotVPN service-worker DevTools console to read the currently-selected apiHost out of chrome.storage.local and to manually re-probe each of the five hard-coded domains. Useful for confirming which backend your install is talking to.
- Chrome with DotVPN installed
- chrome://extensions Developer mode
// dotvpn-host-probe.js// Open chrome://extensions, enable Developer mode, click the service-worker// link for DotVPN (kpiecbcckbofpmkkkdibbllpinceiihk), then paste this into// the DevTools console.(async () => { const { apiHost, installId } = await chrome.storage.local.get(['apiHost', 'installId']); console.log('Currently-selected apiHost:', apiHost); console.log('Persistent installId :', installId); const probes = [ 'dotvpn.com', 'auth-secure-socket.com', 'dot-security-systems.com', 'apache-iv.com', 'tellmar.com', ]; console.log('Re-probing each host with /init?id=' + installId + '&probe ...'); for (const host of probes) { try { const t0 = Date.now(); const res = await fetch('https://' + host + '/init?id=' + installId + '&probe', { headers: { 'Content-Type': 'application/json' }, }); console.log(host.padEnd(28), res.status, (Date.now() - t0) + 'ms'); } catch (e) { console.log(host.padEnd(28), 'error:', e.message); } }})();- 1Install DotVPN.
- 2Open chrome://extensions, enable Developer mode.
- 3Click DotVPN's service-worker link.
- 4Paste the script into DevTools console, press Enter.
- 5Output prints your apiHost and the live status of all five hosts.
DotVPN sends your persistent install ID to undisclosed backend domains
On install, DotVPN generates a permanent ID and sends it to five backends, but only dotvpn.com is named in the listing.
Whichever answers first becomes the backend.
Confirmed: auth-secure-socket.com got it, then location and usage stats.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You install DotVPN, or Chrome restarts the extension's service worker.
This runs the extension's install-initialization routine before you've configured or connected to anything.
The extension creates a persistent installer ID and sends it to five hard-coded domains, three of which are never named in DotVPN's Chrome Web Store listing.
Whichever undisclosed domain answers first is saved and becomes the destination for all of your ongoing usage telemetry.
The install-time host broadcast:
installInit (deobfuscated)
// worker.js — installInit()async function installInit(suffix) { const path = '/init?id=' + (await storage.get('installId')) + (suffix ? '&' + suffix : ''); for (const host of ['dotvpn.com', 'auth-secure-socket.com', 'dot-security-systems.com', 'apache-iv.com', 'tellmar.com']) { try { const res = await fetch('https://' + host + path, { headers: { 'Content-Type': 'application/json' } }); if (!res.ok) return; switch (host) { case 'apache-iv.com': case 'auth-secure-socket.com': case 'dot-security-systems.com': storage.set('apiHost', 'https://' + host); } } catch (_) {} }}Startup call site (deobfuscated)
// worker.js — runs on every service-worker startupasync function onStartup() { if (!(await storage.get('installId'))) { await storage.set('installId', generateUuid()); } installInit('install'); // sends the ID to all 5 hosts, every startup — not just first install chrome.runtime.setUninstallURL('https://tellmar.com/dotvpn/?id=' + (await storage.get('installId')));}HTTP 204 No Content. The response alone is enough to make the extension adopt auth-secure-socket.com as its ongoing backend for the rest of the install.
- Content-Type
- application/json
- Your install IDa2089de1-a462-11f1-8502-2318deec8a38
The same permanent ID generated at install, reused as the key that ties every later request back to this one install.
- Your approximate locationcountry: US, lat/lon: 37.77, -122.41
Your country and coordinates, derived from your IP, are included in the extension's status calls to the same undisclosed domain.
- Bandwidth and quota statssession quota: 900s, 1 bonus/day
Usage and quota figures for your account are also sent to the undisclosed host as part of the same ongoing requests.
- dotvpn.com
Branded, disclosed domain. Probed first; also receives the install ping.
- auth-secure-socket.com
Candidate backend host, not named in the Chrome Web Store listing. Captured live as the domain that received the install ping and was then adopted for ongoing telemetry.
- dot-security-systems.com
- apache-iv.com
Candidate backend host. Not named in the Chrome Web Store listing.
- tellmar.com
Probed at install and used as the uninstall-survey host. Not named in the Chrome Web Store listing.
Run this in the DotVPN service-worker DevTools console to read your persistent install ID out of storage and confirm which of the five backend hosts is currently receiving it.
- Chrome with DotVPN installed
- chrome://extensions Developer mode
// dotvpn-install-id-probe.js// Open chrome://extensions, enable Developer mode, click the service-worker// link for DotVPN (kpiecbcckbofpmkkkdibbllpinceiihk), then paste this into// the DevTools console.(async () => { const { installId, apiHost } = await chrome.storage.local.get(['installId', 'apiHost']); console.log('Your persistent install ID:', installId); console.log('Domain currently receiving it:', apiHost); const hosts = ['dotvpn.com', 'auth-secure-socket.com', 'dot-security-systems.com', 'apache-iv.com', 'tellmar.com']; for (const host of hosts) { try { const res = await fetch(`https://${host}/init?id=${installId}&probe`, { headers: { 'Content-Type': 'application/json' }, }); console.log(host.padEnd(28), res.status); } catch (e) { console.log(host.padEnd(28), 'error:', e.message); } }})();- 1Install DotVPN.
- 2Open chrome://extensions, enable Developer mode.
- 3Click DotVPN's service-worker link.
- 4Paste the script, press Enter.
- 5Output shows your install ID and current domain.
DotVPN reports every popup interaction to apache-iv.com
We observed DotVPN POST to apache-iv.com/3/user/action on popup open, server clicks, and other UI events.
Each request carries the installId and action name.
The endpoint isn't api.dotvpn.com, but one of three undisclosed hosts.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You open the DotVPN popup, click a server location, sign in, or interact with the free-time flow.
Triggered events include 'open', 'connect', 'disconnect', 'signin', 'signup', 'free-load-time-btn', 'free-wait-time-view', 'free-got-time-view', 'free-time-connect-btn', and several upgrade-button clicks.
The extension POSTs your installId and the name of the event to apache-iv.com/3/user/action.
Each POST also includes a 'value' field with extra context, such as the server location code when you click a flag.
204 No Content. Server stores the installId+action+timestamp tuple.
- Content-Type
- application/json
{ "id": "649fa3d1-77c8-4f1c-9f02-1a6b4d8e3c40", "action": "open"}- Your install ID649fa3d1-77c8-4f1c-9f02-1a6b4d8e3c40
A UUID generated on first run and never rotated, letting the backend tie every interaction over the install's lifetime to one user.
- What you didconnect
A short label naming the UI event, such as opening the popup, clicking a server, or interacting with the free-time flow.
- Action contextus-east
Optional second field that carries the location code (for 'connect') or other event-specific detail.
The function that sends every event:
saveAction (deobfuscated)
// worker.js:583 — POSTs {id, action, value} to <apiHost>/3/user/actionasync function saveAction(action, value) { try { const res = await fetch( (await storage.get('apiHost')) + '/3/user/action', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: await storage.get('installId'), // persistent UUID action: action, // 'open' | 'connect' | ... value: value // location code, etc. }) } ); if (!res.ok) return; } catch (_) {}}Every popup open fires saveAction('open')
// popup.js:3986await storage.get('firstRun') && storage.set('firstRun', false);worker.saveAction('open');Every server-location click fires saveAction('connect', <code>)
// popup.js:4176worker.saveAction('connect', locations[code] ? code : null);- apache-iv.com
Default backend host. Receives every POST /3/user/action carrying the installId + event name.
- auth-secure-socket.com
- dot-security-systems.com
Alternate backend selected during installInit. Same /3/user/action endpoint shape if chosen.