Skip to Content
QR Code Security

QR Code Security

SingleForm protects against QR code spoofing with cryptographic verification tokens. Every QR code includes a token that the mobile app validates before loading the form, ensuring the QR code was generated by the form’s owner.

Why QR Verification Matters

Without verification, an attacker could create a QR code that points to a legitimate SingleForm form ID but includes malicious metadata or redirects users to a phishing form. The verification token proves the QR code was generated by someone with access to the form’s signing secret.

How It Works

Token Generation

Each form gets a deterministic 8-character verification token:

  1. Compute HMAC-SHA256(formId, QR_VERIFY_SECRET)
  2. Map the first 8 bytes of the HMAC digest to characters from A-Z0-9
  3. Embed the token as a ?v= query parameter in the QR code URL

The token is deterministic — the same form ID always produces the same token. This means all QR codes for a given form share the same verification token regardless of when they were generated.

Token Validation

When the mobile app scans a QR code:

  1. Extract the v parameter from the URL
  2. The app sends the form ID and token to the backend
  3. The backend recomputes HMAC-SHA256(formId, QR_VERIFY_SECRET) and derives the expected token
  4. Tokens are compared using a timing-safe comparison to prevent timing attacks
  5. If the token doesn’t match, the app warns the user that the QR code may be spoofed

The QR_VERIFY_SECRET environment variable must be set on your backend for QR verification to work. If it’s not configured, verification is gracefully disabled — QR codes will still work, but without spoofing protection.

Token Format

  • Length: 8 characters
  • Character set: A-Z, 0-9 (36 possible characters)
  • Case insensitive: tokens are uppercased before comparison
  • Example: K7M2QX9A

Security Properties

PropertyDetail
AlgorithmHMAC-SHA256
Timing-safeUses crypto.timingSafeEqual to prevent timing side-channels
DeterministicSame form ID always produces the same token
Forgery resistantRequires knowledge of the server-side QR_VERIFY_SECRET
Graceful degradationQR codes work without the secret, just without verification

Spoof Detection

When the backend detects an invalid verification token, it:

  1. Logs a warning with the form ID and the invalid token
  2. Returns a qrVerified: false flag to the mobile app
  3. The app displays a warning to the user before loading the form

This lets users make an informed decision about whether to proceed.

Relationship to Webhook Security

QR verification and webhook signature verification serve different purposes:

QR VerificationWebhook Signatures
ProtectsUsers scanning QR codesYour webhook endpoint
ThreatSpoofed QR codes with fake metadataForged webhook requests
AlgorithmHMAC-SHA256 (8-char token)HMAC-SHA256 (full hex digest)
SecretQR_VERIFY_SECRETSINGLEFORM_SECRET (webhook secret)
Validated bySingleForm mobile appYour server

Both use HMAC-SHA256 but with separate secrets for defense in depth.