Like a man says, "There are no problems, only solutions."

QR Quishing Detector

Frequently asked questions

Which codes does the app read?

Answer

With the camera and from an image you pick, the app reads QR codes, Data Matrix, Aztec and PDF417 codes, and these barcodes: EAN-13, EAN-8, UPC-A, UPC-E, Code 128, Code 39, Code 93, Codabar, ITF, GS1 DataBar, GS1 DataBar Limited and GS1 DataBar Expanded.

Some of these depend on the phone or on how the code is read. GS1 DataBar, GS1 DataBar Limited and GS1 DataBar Expanded are read on an iPhone only, not on an Android phone. Codabar has been read with the camera on an Android phone and on an iPhone, but from an image it has not yet been read on either: the one sample tried was not read. How an iPhone handles a UPC-A code has not yet been checked on a real iPhone.

Four more barcodes, Code 11, MSI Plessey, Pharmacode and Telepen, are read only from an image you pick, not with the camera. The phone's own barcode reader, which the camera uses, has no format for them, so the app reads them itself from the image. Pharmacode is read only the right way up, so a code that is upside down in the image can give a different number: check the number against the pack.

The app's store description and this website's home page say only that it reads QR codes, Data Matrix, Aztec and PDF417 codes and common barcodes such as EAN and Code 128. The notice the app shows before your first scan lists the codes by name and leaves out GS1 DataBar, GS1 DataBar Limited and GS1 DataBar Expanded, which are read on an iPhone only; UPC-A, which has not yet been checked on a real iPhone; and Code 11, MSI Plessey, Pharmacode and Telepen, which are read only from an image you pick and have been confirmed on an Android phone but not yet on an iPhone. The app still reads them where it can.

When a code holds a web address the app can check, the app rates it, as the answer on the risk score below explains. Content that is not an address at all, such as the digits of a product barcode, Wi-Fi network details or plain text, is shown as scanned and marked NOT ANALYSED. That is not a statement that it is safe. The result screen can offer to share such content with another app.

Why does the app not read some codes, such as Spotify Codes?

Answer

For the codes the app does read, see Which codes does the app read?

There are other standard codes the app does not support, such as Micro QR, rMQR, MaxiCode, DotCode and Han Xin. When the app meets one, the camera may show nothing at all or say that it is a barcode it cannot identify; for an image you pick, it may say that it found no code or that it is a barcode it cannot identify.

Some apps and services have a code format of their own, such as Spotify Codes, Snapchat's Snapcodes and Apple's App Clip Codes. The app does not support these formats, and Spotify has published no full specification for Spotify Codes. A Snapcode can open a website, and an App Clip Code holds a web address, so treat what they open with the same care as any other link.

A Spotify Code is the Spotify logo followed by a row of bars of different heights. It does not hold a web address: it holds a short number that only Spotify's own servers can turn into a Spotify address. So a Spotify Code opens something on Spotify. To use one, scan it with the Spotify app.

Turning a Spotify Code into an address would mean asking Spotify's servers. QR Quishing Detector does not do that: the app itself never contacts the address in a code, and the only part of a scan it can send over the internet is a domain name, for the optional domain age lookup described in the privacy policy.

If you know a code is an ordinary QR code and the app does not read it, please report it; the answer to How do I report a problem or suggest a feature? says where.

How does the risk score work?

Answer

For a web address it can check, the app runs a set of checks on your device. Each check looks at the shape of the address for one warning sign, such as a brand name in an address that is not one of that brand's own addresses, a link-shortening service that hides where the link leads, or a user name before an "@" that hides the real destination. The app never visits the site, so it cannot see what is actually there.

Each warning sign a check finds is listed as a finding, and every finding adds its own points to the score; a check can report more than one finding, for example one for each brand name in the address. The score decides the rating: LOW RISK, MEDIUM RISK or HIGH RISK. Some warning signs add more points than others, and when the app recognises a dangerous kind of address, such as a javascript: address, which runs code rather than opening a web page, the result is HIGH RISK.

The result screen shows the rating and the score, and lists what the app found, with the points each finding adds. In the app, Options has a "How the score works" screen that lists every check with its points and the score range of each rating. The domain age never changes the score, and the experimental look-alike brand check, described below, only counts when you switch it on.

Content that is not an address at all, such as plain text, Wi-Fi network details or a contact card, gets no rating: the app shows it as NOT ANALYSED. That is not a statement that it is safe.

The score is an aid to your own judgement. A low score does not mean the link is safe, and a high score is not proof of phishing. When in doubt, do not open the link, and go to the organisation's official site or app yourself.

What does the domain age lookup do, and what does its result mean?

Answer

The domain age lookup tells you how long the domain of a scanned web address has been registered. It is off until you turn it on: in Options, switch on "Look up domain age" and choose "Turn on" in the dialog that explains it. From then on the app looks up the domain of each web address it checks, until you switch it off again.

While the app is running, it remembers the registration dates it has recently obtained, so a later scan of an address on the same domain may be answered from memory, without a new request. When a request is needed, the app sends only the domain name, such as example.com for an address on www.example.com, and never the full address. It sends it over HTTPS to the registry of that domain's ending, and the request reveals your IP address to that registry, as any internet request does. The app never contacts the scanned site itself. The privacy policy describes what is sent and to whom.

The result appears below the rating, in a "Domain age" box. It says how long ago the domain was registered: a recent registration in days and an older one in months or years, rounded; where the real age is not close to the value shown, the box says it is nearly or more than that. When the domain is very new, the box says so, and adds that scam sites are often newly registered but a young domain is not proof of one. For an address on a subdomain that a hosting platform issues, the box gives the platform's age instead, because such subdomains are not registered separately. While the lookup runs, the box says so.

The domain age never changes the score or the rating: it is extra information only.

When there is no age to show, the box says why. "Unavailable for this domain." means the app could not ask a registry about this domain, for example because its ending has no registry lookup service. "Not applicable to this address." means the address has no registered domain name to ask about, for example when it is written as an IP address. In those two cases nothing is sent. "Failed to retrieve." means the app could not obtain usable registration information: for example, the connection failed, the registry did not answer in time, or its answer held no registration date the app could read. A failed lookup is not remembered, so the next scan of that address tries again.

What does the experimental look-alike brand check do?

Answer

The app always checks whether the site's name in an address, such as www.example.com, contains the name of a known brand while not being one of that brand's own addresses. The list of brands is short and centred on Spain: it holds names such as Correos, DGT and BBVA, as well as PayPal and DHL.

The look-alike check goes one step further. Before it looks for a brand name, it treats characters that look alike as the same: some Greek and Cyrillic letters that look like Latin ones, and some digits that look like letters, such as 1 and l, and it ignores hyphens. So it can flag an address that spells PayPal with the digit 1 in place of the letter l, or with a Cyrillic letter that looks like a Latin a, which the brand check above does not see. The result then says that the address resembles the brand: a resemblance, not a match.

Because a resemblance is weaker evidence than the brand's name itself, a look-alike finding adds fewer points than the brand check, but it can still change the score and the rating.

The check is experimental and off by default. Its table of look-alike characters was written by hand and is limited, and the check can flag an address that is not an imitation. To use it, switch on "Look-alike brand names (experimental)" in Options; it applies from the next scan.

It looks only for the brands on its list, it does not look for misspellings in general, and its table of look-alike characters covers only some characters. A result without a look-alike finding does not mean the address is genuine.

How do I report a problem or suggest a feature?

Answer

Report a problem or make a suggestion on the issue tracker on GitHub. In the app, Options has the same link, under "Website and feedback": "Report a problem or make a suggestion".

What you post there is public, and GitHub's own privacy policy applies, so do not post personal data. Questions about privacy can go by email instead: the privacy policy gives the address.