Security
How QRalo verification works
Each code carries its own Ed25519 signature. Scanning it opens qralo.com, which checks that signature against your organization’s public key before it shows a name or an address.
https://qralo.com/verify?qr=eyJ2IjoxLCJpZCI6IjdjMWU0YjJhLTBkNWYtNGU4Yi05YTYxLTNmMmQ4YzBiNWUxNyIsInVybCI6Imh0dHBzOi8vZXhhbXBsZS5jb20vcGF5L21ldGVyLTA0MjMiLCJjb250ZXh0IjoiUEFSS0lORyIsIm9yZyI6ImIzYTlkNmUwLTQxYzItNGY3YS04ZTE1LTZkMmM5MGE0ZjM4MSIsImlhdCI6MTc5MzYxMTIwMCwiZXhwIjoxODI1MTQ3MjAwLCJsb2MiOiJNZXRlciAwNDIzIiwic2lnIjoibVN0cVhLc0FDeHFRdGdqZk8rem5xYmxadUVIeElWaTBJSjZ3ODZBWGtyemgwTkpWbDMrTUYwZzRwSG5HcGxzeUkrNjdVZlZzV0FSc3h1UGZmSStzQ0E9PSJ9
01Anatomy
What’s in a signed code
The part after qr= is JSON, encoded in base64url. Anyone can decode it and read the destination. Nothing in it is encrypted.
- "v": 1,
- Protocol version.
- "id": "7c1e4b2a-0d5f-4e8b-9a61-3f2d8c0b5e17",
- The code’s own ID. The server uses it to look up whether the code is active, expired or withdrawn.
- "url": "https://example.com/pay/meter-0423",
- The destination. Change it and the signature no longer matches.
- "context": "PARKING",
- The purpose you picked: MENU, PARKING, PAYMENT, DELIVERY, EVENT or OTHER.
- "org": "b3a9d6e0-41c2-4f7a-8e15-6d2c90a4f381",
- The organization that signed the code. The server uses it to find the public key.
- "iat": 1793611200,
- When the code was signed, in Unix seconds. Here, 2 November 2026 at 09:20 UTC.
- "exp": 1825147200,
- Optional expiry date. After it, the page shows the code as expired. Here, 2 November 2027.
- "loc": "Meter 0423",
- Optional label, so people can match the code to the meter or table in front of them.
- "sig": "mStqXKsACxqQtgjfO+znqblZuEHxIVi0IJ6w86AXkrzh0NJVl3+MF0g4pHnGplsyI+67UfVsWARsxuPffI+sCA=="
- The Ed25519 signature over every field above. 64 bytes, 88 characters in base64.
The signature covers the exact text of every field except sig. Edit one character, say Meter 0423 to Meter 0424, and the check fails.
02On each scan
What qralo.com does before it shows anything
Six checks, in this order, every time someone scans.
Decode
The camera opens qralo.com/verify and our server decodes the payload. Anything that isn’t a QRalo payload stops here.
Find the key
The server looks up the organization named in org and loads its public key. An unknown organization means an invalid code.
Check the date and the signature
A code past its exp date shows as expired. Otherwise the server checks the Ed25519 signature against the public key.
Check the status
A code you revoked shows as withdrawn. On the Free plan, a code past 100 scans shows as paused.
Check the link
The destination goes through the link checks in section 04. The server also looks up whether the organization verified its domain.
Show the result
The name and destination appear only when the signature is valid. The page opens the destination by itself only for a verified domain and a low-risk link, after a 5-second countdown.
Every check runs online. A revoked code shows as withdrawn from the next scan, wherever it’s printed. Without a connection, a phone can’t check a code.
03Before you print
What a valid signature tells the person scanning
It shows
- The code was signed by the QRalo account on screen, and whether that account verified its domain with a DNS TXT record.
- Nobody has changed the destination, purpose, label or dates since the code was signed.
- Whether the code is active, expired or withdrawn right now.
It can’t show
- Whether a fake was stuck over your code. A fake can’t open qralo.com with your verified name, so put “Scanning opens qralo.com first” on your signs. Look-alike addresses such as qra1o.com are a warning sign.
- Who runs an account with no verified domain. Until the domain is verified, the name is whatever the account typed.
- Whether the destination sits on the verified domain. QRalo doesn’t compare the two yet.
- Whether a website is harmless. The link checks in section 04 flag common phishing patterns.
A photocopy of a genuine code still checks out, because it is the same signed code with the same destination. A scammer’s own code can’t carry your verified name.
04At scan time
What the link checks look at
On every scan, the server checks the destination and gives the verification page a risk level.
The checks are heuristics plus public lists. A brand-new phishing site may not be on any list yet, and a legitimate site can get flagged.
- Whether the destination uses HTTPS, and whether its TLS certificate can be validated.
- Link shorteners, which hide the real destination.
- Raw IP addresses and local addresses used in place of a domain name.
- Keywords and top-level domains often used in phishing.
- Look-alikes of a short list of well-known brands.
- The PhishTank and URLhaus public threat lists, when those services respond.
- When a code is created, its destination is also checked against Google Safe Browsing and PhishTank, when those services are available.
When a public list flags a genuine code
05Keys and accounts
Keys, revocation and accounts
One key pair per organization
When you sign up, QRalo generates an Ed25519 key pair for your organization. The private key is stored encrypted with AES-256-GCM and only signs your codes. You never handle it, so you depend on our servers to keep it secret.
Revocation and expiry
Revoke a code in the dashboard and the next scan shows it as withdrawn, wherever it’s printed. The expiry date sits inside the signed payload, so a later date means a new code. A new destination means a new code too.
Accounts
Passwords are stored as bcrypt hashes. You sign in with email and password, or with Google. Sign-in attempts and code creation are rate-limited. Our servers are hosted by Hetzner in Germany.
Not available yet
Two-factor authentication, single sign-on, an audit log and key rotation. QRalo has not been through a third-party security audit or certification.
06Privacy
What a scan records
Scanning needs no account. Each check records four things, for the issuer’s statistics and to limit abuse.
Legal bases, retention and your rights are in our Privacy Policy.
- Time and result
- When the code was scanned and what the verification page showed.
- Browser information
- The user-agent string, from which we derive the device type and browser.
- Pseudonymized IP address
- A salted SHA-256 hash of the IP address, shortened. It is pseudonymized and can still count as personal data.
- Approximate location
- City, country and approximate coordinates. To get them, the IP address is sent at scan time to ipapi.co, a third-party geolocation service.
07Responsible disclosure
Report a vulnerability
Found a security issue in QRalo? Email us with “Security” in the subject line, with the steps to reproduce it.
- Test only with accounts and codes you own.
- Don’t access, change or delete other people’s data.
- Don’t run denial-of-service tests or automated scans that slow the service down.
- Give us reasonable time to fix the issue before you make it public.
We’ll acknowledge your report and keep you posted on the fix. There is no paid bug bounty.