Can a QR code be hacked?
The symbol cannot do anything. Everything dangerous about a QR code is what the text says and what your phone does next.
What this covers
- Why the format itself carries no executable content
- A table of the attack shapes that occur, what each relies on and what stops it
- Why no scanner can verify who made a code
- Inspecting a printed code for tampering, and what a reader preview does not tell you
- Checks for people scanning, and precautions for people printing
What it does not
- Incident counts and loss figures, which this page will not quote because it has not measured them
- Specific vendor readers, whose preview behaviour changes between releases
- Malware analysis: what happens after you open a hostile page is outside what a QR guide can cover
The symbol is data, not code
A QR symbol holds at most 2,953 bytes, and decoding one produces text. The format has no field for a script, no attachment, no permission and no capability. There is nothing in the standard that can run.
So the honest answer to the question is: the code cannot be hacked, because there is nothing in it to compromise. Every real attack works on the two things around it — what the decoded text says, and what the device does with it automatically.
Where the risk actually is
| Attack shape | What it relies on | What actually stops it |
|---|---|---|
| A sticker over a legitimate code | The credibility of the meter, table or terminal it is stuck to, and nobody looking at the paper | Inspecting the physical code before scanning, and replacing the printed piece when it has been touched |
| A domain that reads like the brand's | The reader preview being skimmed, or not read at all | Reading the host from the right: the last two labels before the first slash |
| A Wi-Fi payload for a network the attacker runs | A join prompt naming an SSID that sounds like it belongs to the building | Only joining a network you would expect to exist there, and never trusting the name alone |
| A payment URI carrying someone else's address | Wallet addresses being unreadable to human eyes, so substitution is invisible | Confirming recipient and amount inside your own wallet or banking app, against a source you already trust |
| A tel: payload to a premium-rate or social-engineering line | The call confirmation being tapped without the number being read | Reading the number the reader shows before tapping call |
| A deep link into an app you are already signed in to | The app performing the action without asking again | Opening the app yourself rather than through the link, and keeping the app and the OS updated |
| A code in an unsolicited letter, email or parcel | A printed square looking more official than the same link would | Treating it exactly as you would treat a link from that sender |
Notice what every row has in common: the code is the delivery, and the trust comes from the surface it is printed on. A sticker on a parking meter borrows the credibility of the meter. This is why the countermeasure is mostly physical and social rather than technical, and why the middle column is the one worth reading — remove the assumption and the attack stops working.
What the format cannot do for you
There is no signature in QR. Nothing in the standard lets a scanner establish who produced a symbol or whether it has been altered, and no reader can display a verified author, because there is nothing to verify against. Anyone with any generator can produce a code decoding to any string, including a string identical to yours.
There is also no revocation and no expiry. A static code printed today decodes the same way in ten years, whether or not you still want it to. That permanence is a feature when you own the destination, and it is the reason a compromised destination is a problem you fix at the destination rather than at the code.
Inspecting a printed code, and reading the preview
Since nothing in the symbol identifies its author, the two things you can actually examine are the object it is printed on and the string your reader shows. Both are more informative than they look, and neither takes longer than the scan itself.
Start with the geometry. The three large squares in the corners are finder patterns, the alternating single-module lines running between them are the timing patterns, and on everything above the smallest version a smaller square sits near the fourth corner as an alignment pattern. Around all of it lies the four-module quiet zone. That structure is fixed, which makes it useful for a purpose it was not designed for: a genuine code was printed once, on one surface, in one pass, so anything that breaks that story shows.
- Run a fingernail around the edge. A sticker laid over a printed code has an edge that lifts, and often a second quiet zone nested inside the original one.
- Look at the white, not the black. The light modules and quiet zone of an applied sticker are a different white from the paper, enamel or plastic beneath, and the mismatch shows most at a shallow angle in daylight.
- Look at the finish. The modules of a code printed with the piece carry the same gloss, screen and ink spread as everything else on it; a code printed elsewhere and stuck on rarely matches.
- Look at the placement. A code applied by hand often sits slightly rotated against the panel it is on, or covers printed text that was clearly meant to be read.
- Where a code is laminated, engraved or printed into the artwork, tampering has to be cruder — one reason those are worth the extra cost on anything unattended.
- On meters, charging points, tables and posters in public places, treat all four checks as routine rather than as suspicion. That is the surface these attacks need.
Then read the preview properly. It shows the decoded string, often truncated, and some readers show only the host. That is enough to identify the site and nothing else. It does not follow redirects, so where a shortened link ends up is invisible until you open it. It does not tell you who owns the domain, because no such statement exists to display. And the string can be arranged to mislead: a long subdomain can put any familiar name at the left of it, and an @ inside the address makes the real host whatever follows the @. Read the last two labels before the first slash, and disregard the rest of what is on screen.
One more thing the preview does not do is appear every time. A reader that recognises a Wi-Fi payload offers a join prompt, and one that recognises a contact card offers a save sheet; neither is a URL and neither shows you a domain. Those are exactly the payloads where the decision has to be made from the situation — whether this building plausibly runs a network by that name — rather than from anything on the screen.
Checks before you act on a scan
- Read the domain your reader shows, from the right: the last two labels before the first slash are the actual site. Everything to the left of them can be made to say anything.
- Look at the physical code. A raised edge, a mismatched paper finish, a square covering printed text or a code stuck at an angle are all worth a second look.
- Treat a code in an unsolicited letter, email or parcel exactly as you would treat a link in one.
- Never sign in to an account from a page you reached by scanning something you were not expecting. Open the app, or type the address yourself.
- For a payment, confirm the recipient and the amount inside your own banking or wallet app, and check a crypto address at both ends against a source you already trust.
- Do not join a Wi-Fi network from a code unless a network by that name is one you would expect to exist in that building.
One technical caveat, stated because it is true rather than because it is likely: reading a code means running a camera frame through image and matrix decoding software, and decoders have bugs like any other software. Keeping the phone updated is the entire mitigation, and it is the same mitigation that covers every other image the camera parses.
If you are the one printing codes
- Print the destination in readable text beside the symbol, so anyone can compare what the reader shows against what you intended.
- Encode a link on your own domain rather than a generic shortener, so the preview a reader shows is checkable by a stranger.
- Seal or laminate codes in public places and inspect them on the same schedule you inspect anything else outdoors.
- Do not build a process that asks people to sign in or pay purely from a scan, with no way to reach the same page independently.
- If a code is tampered with, replace the printed piece. There is nothing to revoke.
A last point about generators, including this one. A code generated in your browser is built from bytes you typed and never leaves the page, so there is no upload to intercept and no stored copy to leak. That removes one link in the chain; it does not make the destination you chose any safer than it was.