A printed square is a link nobody can read. By the time the phone has decoded it, followed two redirects and painted a login form, the user has already committed to trusting it. Screening the resolved host against a database of DNS-verified, currently-live phishing domains puts a decision point back where the human lost one.
Quishing is not a new kind of phishing page. It is the same page delivered through a channel that strips out the inspection step, the gateway and, very often, the managed device as well.
Decoding is instant and silent. The camera turns a pattern of black modules into a string, the operating system offers a tap target, and a browser opens. Most people never see the string at all, and when a preview is shown it is truncated to fit a notification banner — usually to the point where the interesting part of the hostname has been cut off.
None of the defences an organisation has spent a decade building are in that path. No secure email gateway rewrote the link. No proxy inspected the request. No desktop hover-preview gave the user half a second to notice that the domain was wrong. Frequently the device is a personal phone that the organisation has never enrolled and cannot see.
There is a gap of a few hundred milliseconds between decoding the payload and rendering the destination. That gap belongs to whoever wrote the scanner. It is the last place where software still holds the URL as data rather than as a page that is already loading, and it is the natural place to ask a simple question about the host.
The question is deliberately narrow. A single GET /api/v1/check answers it against a database of more than 200,000 active domains, returning is_phishing, a category of phishing/malware, a dns_status of resolves and a confidence of 0.98 on a confirmed hit.
The reason quishing has been so productive for attackers is not technical sophistication. It is that a printed code is trusted the way a physical object is trusted. People have been taught, correctly, to be suspicious of unexpected email attachments and of links that arrive from strangers. Nobody has been taught to be suspicious of a sticker on a parking meter, because for most of the last decade the sticker on the parking meter was genuinely put there by the city.
That trust transfers cleanly to the digital destination. When a user scans a code on a table tent in a restaurant, the mental model is "I am looking at the menu", not "I am about to type my card details into an arbitrary web server chosen by whoever had physical access to this table in the last six weeks". The user's judgement about the code and their judgement about the page have been collapsed into one act, and only one of the two was informed.
It also matters that the destination arrives with no return address. An email carries a sender, headers, an authentication result and a path through infrastructure you control. A QR code carries a string. If it turns out to be malicious, there is nothing to trace back except the physical object, which is anonymous by construction and which the attacker peeled off and threw away a week ago. This is why so many quishing investigations end without an origin: the delivery mechanism does not log.
Why quishing cases go coldCompare this with the desktop web, where the same underlying credential-harvesting kit is often blocked by three independent layers before it renders. On the phone, after a scan, there is usually nothing. That asymmetry is why an organisation that has already invested in email gateway screening and DNS-level filtering can still be losing credentials through a channel it has never instrumented.
The delivery surfaces are wildly different in look and feel, but every one of them ends in the same event: a hostname resolved by a phone with no gateway in front of it.
Parking meters, EV charge points and bike-share docks all carry a payment code, and all of them sit unattended in public. Printing a vinyl sticker at the same dimensions and pressing it over the original takes seconds and leaves no trace beyond an adhesive edge nobody inspects.
Hospitality normalised scanning a code to see a menu, then to pay. A table tent is small, replaceable and handled by hundreds of strangers a week. The resulting page can ask for a card number without looking remotely out of place, because that is what the real one does too.
Link rewriting operates on hyperlinks. A code rendered as pixels inside an attached PDF, PNG or embedded signature image contains no hyperlink to rewrite, so it travels through untouched and is read by a second device the mail system cannot see.
Attendees expect to scan at events: to join the Wi-Fi, download the agenda, exchange contact details or claim a session recording. Signage is temporary, produced in a hurry by a third party, and installed by people who cannot verify what any of it points at.
"Scan to pay" on paper correspondence is now routine for utilities, councils and healthcare billing. A convincing counterfeit bill posted to a household needs no infrastructure at all beyond a domain and a payment form, and it arrives with the authority of physical mail.
Genuine authenticator enrolment uses a QR code, which has trained users to scan one during a security workflow. An email claiming that multi-factor authentication must be re-registered, with a code attached as an image, borrows that muscle memory precisely when the user is most compliant.
What unites these is that the attacker chooses the placement, not the payload delivery. In email phishing the adversary has to get past your filters. In quishing they have to get past a maintenance worker who is not looking, or a print vendor who does not check, or a conference organiser with four hours until doors open. The economics are attractive because the physical placement cost is near zero and the reach is whoever walks past.
| Dimension | Email phishing | Quishing |
|---|---|---|
| What must be defeated | Gateway filtering, URL rewriting, attachment detonation | A maintenance worker, a print vendor or an event organiser |
| Cost per extra target | Cheap, but throttled by sending reputation | Effectively zero — one sticker reaches everyone who walks past |
| Device that opens it | Usually managed, usually logged | Usually a personal phone with no enrolment |
| Forensic trail | Headers, authentication results, transport logs | An anonymous printed object and nothing else |
| Durable indicator | Sender, subject, hash, destination host | The resolved destination host |
The attachment case deserves special attention because it is where quishing intersects most awkwardly with existing controls. A security team can reasonably say that their mail path inspects links, detonates attachments and rewrites URLs. All of that remains true and none of it helps, because the malicious instruction is an image of a link, and the click happens on a device that is not part of the conversation. Nothing sees both halves.
The MFA-enrolment variant is the most damaging in practice because of what it collects. A user who believes they are re-enrolling an authenticator will not only supply a password, they will actively co-operate with a prompt for a one-time code, and they will do so quickly because security workflows feel urgent. That is a full session takeover delivered by a user who thinks they are doing exactly the right thing, which is why awareness training alone cannot carry this problem.
None of this changes the shape of the technical defence. Whatever the surface, whatever the pretext, the attack terminates at a hostname. If that hostname is already known and currently resolving as phishing infrastructure, the scanner has everything it needs to refuse before the page is fetched.
Every other phishing control benefits from software's great advantage: you can change it everywhere at once. A printed code is immune to that, and planning around the difference changes what "response" means.
Consider what happens when a malicious code is discovered on a payment terminal in one of two hundred sites. In a software incident you would push a rule and be done. Here, the artefact is a physical object bonded to a physical asset in a location that may be unstaffed, and remediation means dispatching a person with a scraper. Until that person arrives, the code keeps working, and everyone who scans it in the interval is exposed.
The attacker who covered one meter almost certainly covered a dozen, distributed across a district according to footfall rather than according to your asset register. The first report tells you the campaign exists; it tells you nothing about its extent. This is the inverse of the usual situation, where you can query for the indicator and get a complete list in seconds.
The practical consequence is that physical remediation should never be your only control, because it cannot be fast and cannot be exhaustive. What can be both is the destination. Peeling the sticker removes one instance; getting the destination host recognised as phishing infrastructure neutralises every instance simultaneously, including the ones you have not found, for every user whose scanner consults a threat source. The physical clean-up still has to happen, but it stops being the thing standing between your customers and a credential form.
The daily rebuild that lands at 04:30 UTC only includes domains with an active A record, so a domain that has been abandoned drops back out and stops consuming attention. For an estate operator, that means the list you push to venue Wi-Fi filtering or to your inspection tooling stays proportionate to what is actually live, rather than accumulating a decade of dead indicators. The same discipline that makes the feed useful for DNS filtering makes it useful here.
Because the physical artefact is anonymous, the destination host is frequently the only durable evidence you will ever have. Photograph the code in place, record the decoded payload, record the full redirect chain and the final host, and keep the API response including its last_checked date. That package is what lets you demonstrate later that a specific site was compromised on a specific day, which matters for insurers, regulators and any eventual incident response narrative.
The single most common integration mistake is checking the domain that was encoded in the code. Almost none of the interesting ones encode the destination directly.
A QR code has a payload size limit, and every extra character makes the module grid denser and harder to scan from a distance or in bad light. That practical pressure pushed the whole ecosystem towards shorteners years ago, long before anyone was thinking about abuse. The result is that the host encoded in the image is usually a link-shortening or campaign-tracking service, and checking it tells you only that the shortener exists.
Issue the request with redirects disabled, read the Location header, and follow it yourself with a hard cap — five hops is generous, and anything longer is itself a signal worth surfacing. Extract the host from the final URL, normalise it to lowercase, strip any port, and pass that to /api/v1/check. Do not send the full URL and hope: the endpoint will strip http://, https:// and path segments for you, but it cannot un-shorten on your behalf, because that requires a network request from your side.
A chain that passes through a domain already flagged as phishing infrastructure is suspicious even if the terminal host is unknown, because attackers reuse redirector infrastructure far more readily than they reuse landing pages. With a batch of up to 100 domains per request at one lookup each, submitting an entire chain costs the same as three or four individual lookups and gives you a far more complete picture.
A shortened link that resolves to the same host for every user is a natural candidate for a short-lived local cache keyed on the shortened URL. This is where the arithmetic gets friendly: a venue whose printed campaign generates tens of thousands of scans a day is resolving a handful of distinct destinations, so the number of distinct hosts you actually need to check is orders of magnitude smaller than the number of scans.
First, cloaking: some destinations serve a redirect to a benign page when the requesting client looks like a scanner or a datacentre IP, and the real payload only to a mobile user agent. If your unroller always presents as a server, you will systematically see the safe branch. Second, timing: a chain that hangs is a denial-of-service against your own scan path. Set an aggressive timeout, fail towards showing the user a warning rather than silently opening, and never let the check block rendering indefinitely.
The order of these steps matters more than any individual one. Each stage is cheap on its own; together they turn an unmediated scan into a decision with evidence behind it.
Hold the decoded string as data. Do not hand it to the system URL handler yet. This is the whole opportunity: once the OS opens it, the request has left your process and the destination is being fetched by a browser you no longer control.
Send the payload to your own backend, which follows redirects with a hop cap and a short timeout. Keeping this on the server matters twice over: it stops the phone making requests to hostile infrastructure, and it keeps your API key off the device, which the API documentation is explicit about.
Batch the hosts you collected into a single POST /api/v1/batch call. The response gives you per-domain results plus checked, phishing_found and credits_used, which is exactly what you want to log alongside the scan event.
On a hit, replace the "Open" affordance with an interstitial that names the host and says plainly why it is blocked. On a miss, show the resolved final host in full before the tap target, because the point of the whole exercise is to give back the preview the format removed.
Log the payload, the chain, the final host and the verdict. Aggregate by location if your product knows where the scan happened. A cluster of identical malicious destinations scanned within a few streets of each other is a physical campaign, and that is actionable in a way a single event never is.
# 1. unroll, capped at 5 hops, then take the final host FINAL=$(curl -s -o /dev/null -w '%{url_effective}' -L --max-redirs 5 \ --max-time 4 "https://short.example/x7Kq2") HOST=$(printf '%s' "$FINAL" | awk -F/ '{print $3}') # 2. ask the one question that matters curl -s "https://phishingdetectionapi.com/api/v1/check?domain=$HOST&apikey=YOUR_KEY" # {"domain":"..","is_phishing":true,"category":"phishing/malware", # "dns_status":"resolves","confidence":0.98,"last_checked":"2026-07-27"}
Scanner-side checking protects the person holding the phone. These three workflows protect everyone else — the people who print the codes, the people who own the walls they hang on, and the network they connect to.
Start with the print run, because it is the cheapest possible intervention. A marketing team producing a campaign has a finite list of destination hosts, usually fewer than a dozen and often exactly one. Screening them before the artwork goes to press costs a handful of credits and eliminates the scenario where a brand ships fifty thousand pieces of print pointing at a domain that a partner agency registered, let lapse, and which somebody else has since picked up. The check takes seconds; the reprint takes a quarter.
If your organisation knows the exact set of hosts its own codes are supposed to reach, a scanner or an MDM policy can treat anything outside that set as worth a second look. This is the strongest possible signal in a venue context: a stadium that publishes four campaign domains has no legitimate reason for a code on its own concourse to resolve anywhere else. The API answers "is this known-bad"; the allowlist answers "is this ours"; the interesting cases are the ones that are neither.
Estate audits close the loop on the physical side. Give inspection staff a scanner app that logs the decoded payload and final host of every code they walk past, then reconcile that log against the allowlist on a schedule. Fuel forecourts, transit stations, car parks and hospital concourses all already run routine physical inspections; adding a scan takes seconds per asset and turns an invisible problem into a report. For a large estate, batching the collected hosts 100 at a time keeps the whole audit within a trivial credit budget.
The daily threat feed ships the full CSV with domain,category,dns_status columns plus a changelog of what was added and removed, which is exactly the shape a DNS sinkhole wants. Load it into Pi-hole, BIND RPZ or Unbound once a day after the 04:30 UTC build and every device on the guest network inherits the protection, including the ones that scanned a code before you ever heard of it. The feed subscription is a flat $499 per month, so the marginal cost of covering an extra site is zero.
Since the alternative to the feed is per-lookup billing, it is worth being concrete. Pre-press screening and estate audits are naturally small: a few thousand lookups a year, which the Growth package at 10,000 credits covers comfortably, with lookups reset monthly. Scanner-side checking is the workload that scales with users, and that is where local caching plus the feed matter — matching against a locally loaded list costs nothing per scan, and you spend API credits only on hosts the local copy has never seen. The pricing page lists every package above that.
This service answers one question precisely. Designing around its actual boundaries produces a better system than pretending it answers a broader one.
The database contains domains that have been confirmed as phishing infrastructure and verified by DNS as currently resolving. A hit is therefore a strong, specific statement: this host is live phishing infrastructure, categorised phishing/malware, with a confidence of 0.98. A miss is a much weaker statement. It means the host is not in the database today. It does not mean the host is safe, and no responsible integration should render it as a green tick.
That distinction bites hardest in exactly the scenario quishing creates. Physical campaigns often run on domains registered days before the stickers go up, precisely because the operator expects the domain to be burned quickly. A brand-new domain that nobody has reported yet will not be in any known-bad list, including this one. This is not a gap peculiar to this product; it is the structural limitation of reputation lookups as a class, and any vendor claiming otherwise is describing a different kind of system.
So pair the lookup with signals you can compute yourself. Domain age, whether the registrable domain matches your own allowlist, whether the chain crossed more hops than expected, whether the final host differs from the host encoded in the code, whether the page is being requested from a location where you have no campaign running. None of these are verdicts on their own, and all of them are cheap. The API supplies the one input you cannot produce yourself — a continuously maintained view of what is confirmed bad right now — and your own context supplies the rest.
Designing around the boundaryA compromised path on an otherwise legitimate host will not be flagged. There is no screenshotting, no brand-logo matching and no machine-learning judgement about an unknown page. What there is, is a database whose freshness is enforced: entries that stop resolving fall out at the next rebuild, which is why the active count sits above 200,000 while the all-time total is far larger. That churn is the feature. It is also why teams building threat intelligence pipelines tend to treat this as a high-precision enrichment source rather than a sole arbiter.
phishing/malware, DNS status resolves. Block it and say why.The questions below come up in nearly every conversation about instrumenting a scan path.
Both, but the final resolved host is the one that decides. Encoded hosts are overwhelmingly shorteners and campaign trackers, so checking only that tells you almost nothing about the destination. Follow the chain server-side with a hop cap, collect every host you pass through, and submit them together in one batch call. Checking the intermediate hops is worth the extra credits because attackers reuse redirector infrastructure much more readily than they rebuild landing pages, so a flagged hop is often your earliest warning even when the terminal host is still unknown.
Log both hosts against the scan event even when the verdict is clean. A divergence between the encoded host and the final host is not suspicious on its own — that is what a shortener does — but a sudden change in where a long-running printed campaign resolves to is one of the earliest signals that a code has been overlaid or a campaign domain has changed hands.
No. The API key is the username chosen at registration and it must never appear in client-side code, because anything shipped to a device can be extracted from it. Route the check through your own backend, which also gives you the place to unroll redirects, cache repeated destinations, enforce timeouts and apply your own allowlist. The rate limit is 10 requests per second per key, which is a further reason to funnel through a server you control rather than letting thousands of handsets each hold a key.
The dominant cost is unrolling the redirect chain, not the lookup, because following hops means talking to infrastructure you do not control. Cap the hops, set a short timeout, and cache the payload-to-host mapping: a printed campaign generates enormous scan volume against a tiny number of distinct destinations, so most scans should be answered from cache. If the chain times out, fail towards an interstitial that tells the user the destination could not be verified rather than silently opening it.
Take the daily feed and load it into the resolvers behind your guest Wi-Fi. The CSV carries domain,category,dns_status and ships with a changelog of additions and removals, which fits Pi-hole, BIND RPZ, Unbound and commercial DNS appliances directly. Sync after the 04:30 UTC build. This covers every device on the network regardless of which scanner app it uses, which is the only realistic way to reach guests who will never install anything of yours.
Resolver-level blocking only reaches devices actually using your DNS, so a phone on mobile data walks straight past it. Treat the feed as the floor for a venue and the scanner-side or MDM check as the layer that follows the user off the network.
It means the host is not currently in a database of confirmed, actively resolving phishing domains. That is genuinely useful and it is not the same as safe. Domains registered specifically for a physical campaign may be too new to have been reported anywhere. Present a clean result as "not known to be malicious", show the user the full resolved hostname, and keep your own signals — domain age, allowlist match, unexpected chain length, geography — in the decision.
Every destination host in the run, before artwork approval, including any shortener or tracking domain the chain passes through. It is a handful of credits against the cost of a reprint. Then publish that list internally as your allowlist so scanners, MDM policies and inspection staff have something to reconcile against.
A pre-press workflow is one of the smallest possible workloads: a dozen hosts per campaign, a few hundred lookups a year. The Growth package at 10,000 credits, billed monthly, covers a print operation and its estate audits together with room to spare.
No, and the two work on different failure modes. Education helps with pretexts — why an unexpected MFA re-enrolment email deserves suspicion. It cannot help with a hostname the user was never shown, which is the specific thing the format takes away. Restore the preview, block the confirmed-bad destinations automatically, and let training work on the judgement calls that remain. Teams already running awareness programmes usually find the scan telemetry gives them far better material than generic examples.
Pick a monthly plan and wire a single lookup into the moment after decoding. Screen a print run, audit an estate, or sync the daily feed into the resolvers behind your guest network — all of it against a database of DNS-verified phishing domains rebuilt every 24 hours.