We Recovered Our Test Password from a Checker's Five-Character Queries
FreePolicyTools original research · 15 October 2026 · Unpublished draft
A password checker does not have to send the password itself to expose enough information to reconstruct it. In our tests of Kaspersky's public web checker, ordinary typing generated a series of five-character SHA-1 hash prefixes. An offline reconstruction, given only those ordered prefixes, recovered our exact random 16-character test value.
We repeated the result in fresh browser contexts without our inspection proxy, including a faster typing condition. All inputs were disposable research strings that protect no account. This is evidence about the public web checker and our synthetic tests—not evidence of stolen user passwords or a finding about the Kaspersky Password Manager application.
The distinction matters: we did not observe the complete password or its complete hash leaving the browser in any completed live-tool run. The exposure arose from the relationship between successive partial hashes.
What we found
We selected ten public tools before discovery, reached usable interfaces on eight, and completed 42 of 51 field-entry attempts. The other 9 attempts remain marked incomplete. Two additional routes returned verification pages and received no test password.
The main findings were:
- Kaspersky's web checker: one complete-value fill produced one matching lookup; sequential entry produced a reconstructable series. Three completed typing runs, covering two different synthetic passwords and both capture configurations, yielded exact offline recovery.
- PassGen.co: a correction or pause caused an intermediate-value lookup as well as a final-value lookup in the tested conditions. Its related-value limit materially constrained the sequence. We did not recover its complete test password.
- Four explicit-check tools: Have I Been Pwned, Scan.now, PassCheck.io and Flassword sent one complete-input prefix per completed run, after their check action. No matching intermediate lookup was observed.
- Two strength tools: Bitwarden and Security.org produced no matching password, full-hash or known-prefix transmission in completed tests. Several Security.org typing attempts failed input verification, so they cannot be counted as negative evidence.
These are descriptive observations from a small purposive sample. They are not a percentage of the market, a ranking of overall security, or proof that an unobserved transmission is impossible.
Each bar represents one completed visit. Orange denotes a query matching an unfinished or corrected input. Repeated queries count separately; CORS preflights do not count as GET lookups. “Direct” omits this study's inspection proxy while retaining the system's existing network route.
The Kaspersky result: one lookup versus a sequence
On the tested page, the browser sent requests to password.kaspersky.com/ksn/hashes/<five hexadecimal characters>/. The prefix matched the first five characters of the SHA-1 digest of the current input. Kaspersky's publicly served client code independently corroborated that operation: an input listener invokes the breach check, hashes the field value, and fetches that first-party route.
The page explains that its leak check sends a snippet to its server and says that actual passwords are not collected or stored. Our capture cannot determine retention. It can test what the browser sends and whether the resulting sequence carries enough information to reconstruct our dummy input. Kaspersky's checker and explanation
In run kaspersky-slow-r3, the 16-character test and deliberate correction generated 21 matched GET requests: 19 for intermediate values and 2 for the completed value. All matched requests received HTTP 200 responses. Some input states appeared more than once. Repeated prefixes were retained in their observed order.
A selection from the recorded trace illustrates the progression. “Input length” is added for this evidence table; the reconstruction algorithm was not given those lengths.
| Time after input began | Input length | Sent prefix | State | HTTP status |
|---|---|---|---|---|
| +0.170 s | 1 | FE5DB |
Intermediate | 200 |
| +0.897 s | 2 | 1ADCD |
Intermediate | 200 |
| +5.295 s | 8 | 6D235 |
Intermediate | 200 |
| +6.021 s | 9 | D67C4 |
Intermediate | 200 |
| +7.863 s | 8 | 6D235 |
Intermediate | 200 |
| +14.264 s | 16 | 8FDAD |
Complete | 200 |
The reconstruction recovered the exact dummy value from the full sequence in 0.003 seconds on this machine. That timing describes this small controlled example; it is not a benchmark for arbitrary passwords.
The result repeated in a same-input comparison without the inspection proxy. Complete-value fill (kaspersky-paste-direct-r5) produced one lookup; kaspersky-fast-direct-r4 produced 17 matching lookups at a requested 100-ms character delay, and kaspersky-slow-direct-r4 produced 21 at a requested 700-ms delay with correction. Both reconstructed the second complete synthetic value exactly. Direct browser request and response events confirmed these positive observations.
Original, unedited screenshot from the measured visit. The masked input and strength result show the interface state. The request records and reconstruction establish the disclosure finding; this screenshot alone does not.
Why a short hash sequence can reveal a long password
A single five-character hash prefix does not identify a random 16-character password. Many possible values share that prefix. The problem changes when each query represents the previous input plus one character.
For our controlled reconstruction, the first request restricted the first character. Each subsequent request restricted the next small edit. The offline program tested printable ASCII characters against the next observed prefix and retained compatible candidates. It also allowed repeated input and backspace, handling the deliberately introduced correction. The intended password and action log were withheld until the recovered value was graded.
Our program assumes this append-and-correct input model, a printable ASCII alphabet, and gaps of at most three unknown characters. It is not a universal recovery tool. These assumptions nevertheless sufficed for the observed Kaspersky sequences, including our random test values.
The relevant observer is an endpoint or TLS terminator that can read and associate the ordered requests. This experiment does not show that someone passively watching encrypted Wi-Fi traffic can see the query paths. It also does not establish that any provider retained, analyzed, or misused them. A VPN or residential proxy would not prevent the destination from receiving the requests that the browser sends to it.
This underlying risk is already documented. Have I Been Pwned explicitly warns against incremental searches and recommends waiting until entry is complete. Its range API intentionally sends only a short hash prefix, with the remaining comparison performed locally. HIBP API documentation
Quarkslab previously demonstrated incremental-query risks in a different product. Earlier academic work examined both web password meters and compromised-credential checking protocols. Our contribution is the current measurement, live-tool comparison, controlled reconstruction, and released evidence—not the invention or first discovery of the attack. Quarkslab's research, 2015 password-meter study, 2019 credential-checking study
A pause is not always the end of a password
PassGen.co provided a useful contrasting case. Complete-value fill and fast typing produced one final-value lookup in our first pass. The slower sequence, with a correction in the middle, produced two: one representing the temporary nine-character value and another representing the final 16-character value. The nine-character intermediate and final-value pattern repeated in an independent context and the direct-browser control. The supplementary 1,500-ms condition also produced two lookups, representing an unfinished eight-character value and the final value.
The page discloses its use of a five-character fingerprint and limits checks of similar values. The downloaded client script also contained that limit. This protection matters: our observed PassGen sequences were too short for the sequential recovery demonstration used here. We did not establish full-password recovery from PassGen. PassGen's privacy explanation
Its result illustrates a narrower design issue. A timer can tell that typing paused; it cannot establish that the person has finished. Privacy explanations should distinguish local strength scoring from external breach checks and state when those checks occur.
The interface after entry. The released request timeline distinguishes the intermediate query from the final one.
What the other tools did
The table includes every completed live run, including exploratory repairs and direct controls. The numerator is completed visits; the denominator is all field-entry attempts for that provider. Blocked discovery routes are listed separately.
| Public tool | Completed / attempted | GET prefix lookups per completed run | Observed behavior |
|---|---|---|---|
| Bitwarden | 6 / 6 | 0–0 | No matching transmission detected |
| Security.org | 4 / 7 | 0–0 | No matching transmission detected; typing failures retained |
| Kaspersky web checker | 5 / 11 | 1–21 | Per-input first-party hash-prefix sequence; exact dummy recovery |
| Have I Been Pwned | 5 / 5 | 1–1 | Complete-input prefix after Check |
| Scan.now | 5 / 5 | 1–1 | Complete-input prefix to first-party forwarding route after Check |
| PassGen.co | 7 / 7 | 1–2 | Extra intermediate prefix during pause/correction; no full recovery |
| PassCheck.io | 5 / 5 | 1–1 | Complete-input prefix after Check |
| Flassword | 5 / 5 | 1–1 | Complete-input prefix after Check |
Scan.now used its own /api/pwned/<prefix> endpoint. We observed the browser sending the prefix to Scan.now; we did not observe what its server sent downstream. Have I Been Pwned, PassCheck.io and Flassword used the Pwned Passwords range endpoint in our completed tests.
Scan.now's visible report. First-party forwarding should not be mistaken for an absence of external processing.
The check action separated typing from the observed complete-value prefix lookup.
No matching password-related transmission was detected in the completed Bitwarden runs within the tested coverage and observation windows.
NordPass and PasswordMonster returned verification pages in our environment. We did not bypass them, submit a test password, or infer how the underlying tools behave.
How we checked the evidence
We used one isolated research page at a time during live collection. Each visit received a fresh, nonpersistent browser context. Chrome 155 and Playwright 1.62.1 recorded browser events; a loopback mitmproxy capture independently recorded requests and responses. No account login, real credential, global certificate change, or paid proxy credential was used.
Before live entry, independent HTTP and HTTPS receiver fixtures each confirmed 17 of 17 expected requests with matching paths and payload bytes, including worker, service-worker, frame and departure traffic. Browser events alone missed two departure beacons; the proxy captured them. A deliberately incremental local password fixture supplied a separate positive reconstruction control. It is excluded from provider totals.
The first pass used complete-value fill, 100-ms typing, and 700-ms typing with a correction after character eight. Each explicit-check tool received a 15-second pre-check wait. We then observed for 30 seconds, navigated away, and kept proxy capture for another five seconds. Follow-ups included matched-input pairs and direct controls. Requested delays are conditions, not exact wall-clock measurements of every key.
The detector matched our complete and intermediate synthetic values, specified URL/base64 forms, hexadecimal complete SHA-1/SHA-256/MD5 hashes, and known hash-prefix paths. Kaspersky's path was an exploratory extension documented before its first completed entry test. A request merely loading an analytics script was not counted as password disclosure.
Interface failures were preserved. Kaspersky required ordinary Tab navigation because its field label intercepted clicks. Security.org discarded early characters in multiple typing attempts; a later documented priming repair cleared a discarded character before entering a complete new value. The detector and final-field checks prevented failed entry from being presented as a clean negative.
What this study cannot establish
This is one collection session with a purposive sample and controlled ASCII inputs. It does not measure real-world typing distributions, other devices, geographical differences, authenticated tools, downstream retention, historical deployments, or later fixes. Headless automation and the inspection proxy can change behavior; the direct controls repeat the principal positive finding but do not eliminate every environmental difference.
Non-detection is bounded by the tested transformations and capture coverage. Unknown encryption, other transports, or behavior outside the observation window can remain unobserved. The evidence does not establish real-user exploitation or an intention to collect credentials. Results from different tool categories should not be treated as interchangeable security scores.
What should change
For checker developers, the demonstrated failure mode is incremental external lookup. A complete-input action can separate typing from the breach query; local strength scoring need not trigger a request for each edit. Debouncing and query limits deserve explicit tests with pauses, deletions and resumed entry. A statement that the full password never leaves the device does not by itself describe what a sequence of derived values can reveal.
For readers, these findings favor keeping password assessment inside a trusted password manager or a clearly understood local workflow. Avoid entering an active credential into unfamiliar tools. A strong-looking score describes the input's estimated resistance to guessing; it does not audit the checker that received it.
Data, screenshots and disclosure status
The accompanying release includes per-run CSV/JSON, matched request evidence, reconstruction outputs, timing tables, methodology, exclusions, detector controls and reviewed screenshot hashes. All published password values are disposable synthetic strings. Unrelated browsing traffic, cookies, account details, challenge screenshots and certificate keys are excluded.
Run python3 audit-public.py against the released folder to recalculate counts, verify that the prefixes match the synthetic values, and repeat the offline reconstruction without contacting any service. The audit checks the released subset; it does not independently authenticate the private originals.
Provider contact: none as of this draft. Questions for Kaspersky and PassGen are saved as unsent drafts. There is no provider response or verified remediation to report. The article, evidence package and screenshots have not been published or sent externally.