Is Browser History Plus safe?
Browser History Plus is high risk. After a consent prompt, AI search collects your 100 most recent history entries (URL, title, visit time) and sends them to browserhistory.net with a persistent user ID and tracking token. The same IDs are reused on every later search.…
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
AI Search Sends Full Browsing History to Remote Server
After a consent prompt, AI search collects your 100 most recent history entries (URL, title, visit time) and sends them to browserhistory.net with a persistent user ID and tracking token.
The same IDs are reused on every later search.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-200
- Source
- Dynamic sandbox
You click the AI search button after accepting the consent prompt.
A one-time consent modal must be accepted before the first AI search fires.
Retrieves your 100 most recent history entries and POSTs them, with URLs, titles, visit times, a persistent user ID, and a server-assigned hash, to browserhistory.net/api/generate.
The request body can exceed 21 KB and is sent in plaintext JSON.
Returns array of integer indices into indexed_history identifying relevant entries. DA confirmed: POST body was 21,180 bytes and contained 63 real browsing history items plus a planted marker entry.
- Content-Type
- application/json
{ "model": "gemma3", "indexed_history": [ { "index": 0, "title": "GitHub", "url": "https://github.com", "hostname": "github.com", "lastVisitTime": 1749081000000 }, { "index": 1, "title": "Stack Overflow", "url": "https://stackoverflow.com/questions/12345", "hostname": "stackoverflow.com", "lastVisitTime": 1749080900000 } ], "query": "a planted marker value", "userId": "b85c489c-b404-4992-b6a2-e8703beb4783", "userHash": "eyJ1c2VySWQiOiJiODVjNDg5Yy1iNDA0LTQ5OTItYjZhMi1lODcwM2JlYjQ3ODMifQ==", "version": "1.0", "stream": false, "options": { "temperature": 0.3, "top_p": 0.9 }}- History (URL, title, timestamp){index:0, url:'https://github.com', title:'GitHub', hostname:'github.com', lastVisitTime:1749081000000}
Up to 100 of your recent browser history entries, with every URL visited, the page title, and the exact time of each visit.
- Persistent user IDb85c489c-b404-4992-b6a2-e8703beb4783
A randomly generated ID created on install that never changes. It ties every AI search, now and future, to the same server identity.
- User hash (server-assigned token)eyJ1c2VySWQiOiJiODVjNDg5Yy1iNDA0LTQ5OTItYjZhMi1lODcwM2JlYjQ3ODMifQ==
A secondary token assigned by the server during first-run setup. Sent alongside the userId in every AI search request.
- Search querygithub account security settings
The text you typed into the AI search box.
- Extension version1.0
The installed version number, sent with every request.
executeAiSearch(), how the history is collected and sent
getAllHistoryItems — readable form
history.jsfunction getAllHistoryItems() { return new Promise(function(resolve) { chrome.history.search({ text: "", startTime: 0, endTime: Date.now(), maxResults: 100 }, function(items) { resolve(items || []); }); });}sendToAiServer — readable form
history.jsasync function sendToAiServer(query, indexedHistory, userId, userHash) { const response = await axios.post(`${baseUrl}api/generate`, { model: "gemma3", indexed_history: indexedHistory, // full history array query: query, userId: userId, // persistent local ID userHash: userHash, // server-assigned token version: "1.0", stream: false, options: { temperature: 0.3, top_p: 0.9 } }); return response.data;}- browserhistory.net
Receives full browsing history arrays, search queries, and persistent user/tracking identifiers. Operates a Gemma3 LLM instance that processes history contents server-side.
Extension Registers Persistent Tracking ID with Remote Server on Install
On install, and weekly after, the extension contacts browserhistory.net and registers a persistent identifier tied to your browser.
The server returns a userId and tracking token, stored locally and sent with every AI search.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-200
- Source
- Dynamic sandbox
You install the extension.
No further interaction is required, the tracking registration fires automatically.
The service worker POSTs a persistent browser identifier to browserhistory.net/init and stores the server-assigned userId and userHash returned in the response.
This call also repeats automatically once per week, keeping the server record active.
200 OK, server returns {userId, userHash, installDate}. DA confirmed 2 GET/POST requests to /init immediately on extension activation (during dynamic analysis). The assigned userId subsequently appeared in 3 GET requests to /api/tags.
- Content-Type
- application/json
{ "userId": "4c9d96be-6ae0-4f00-976c-5ac903b7628d"}FETCH_INSTALL_DATE, automatic init call on install and weekly thereafter
FETCH_INSTALL_DATE — readable form
background.jsasync function FETCH_INSTALL_DATE({ state, commit }) { const installDate = state.storage.user.installDate; const now = new Date(); const isStale = !installDate || (now - new Date(installDate)) > 6048000; // 7 days if (state.storage.user.installDate && !isStale) return; try { const manifest = chrome.runtime.getManifest(); const initUrl = `${manifest.homepage_url}init`; // https://browserhistory.net/init const body = {}; if (state.storage.user.id) body.userId = state.storage.user.id; const response = await axios.post(initUrl, body); if (response.status === 200) { const data = response.data; if (data.installDate) { let installDate = data.installDate; if (typeof data.installDate === 'number') { installDate = new Date(data.installDate * 1000).toISOString(); } commit('SET_INSTALL_DATE_SYNC', installDate); commit('SET_INSTALL_DATE', new Date().toISOString()); } if (data.userId) commit('SET_USER_ID', data.userId); if (data.userHash) commit('SET_USER_HASH', data.userHash); } } catch (e) { /* silently ignored */ }}initStorage — userId generation on first install
background.jsinitStorage() { // Fires immediately on construction (before onInstalled) store.state.loaded.then(() => { if (!store.state.storage.user.id) { // Math.random().toString(36) + Date.now().toString(36) store.commit('SET_USER_ID', generateRandomId()); } });}Written to storage on install; persists across restarts. userId/userHash reach the server every search, linking future ones to this ID.
- Location
- chrome.storage (Vuex persisted state, user namespace)
{ "user.id": "4c9d96be-6ae0-4f00-976c-5ac903b7628d", "user.userHash": "<server-assigned token>", "user.installDate": "2026-06-04T23:33:53.000Z", "user.installDateSync": "2026-06-04T23:33:53.000Z"}- browserhistory.net
Receives the browser's persistent userId on install and weekly after. Returns a userId, userHash, and installDate used to track this browser across future AI search requests.
What it can do
Permissions this extension asks for, as declared in version 1.0.6. 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>
Store data in your browser
storage
Read and change your full browsing history
history
Clear your browsing history, cache and cookies
browsingData
Start, monitor and manage your downloads
downloads
Read and change cookies, including the ones that keep you signed in
cookies
Schedule its own background tasks
alarms
See the address and title of every tab you have open
tabs