Description
compute_signature in src/utils.py returns hmac.new(...).hexdigest() — a hex string. While HMAC-SHA256 is not directly vulnerable to length-extension attacks (unlike raw SHA256), using a hex digest doubles the output size unnecessarily and diverges from the JWT/PASETO convention of base64url encoding. More importantly, the validate-qr endpoint accepts the raw JSON-encoded QR string and extracts sig by key name, making it possible to craft a valid-looking payload that passes JSON parsing but includes extra unsigned fields.
Requirements & context
- Migrate
compute_signature to return base64.urlsafe_b64encode(hmac_bytes).decode() instead of hexdigest()
- Update
validate_qr to rebuild the unsigned dict by explicitly whitelisting known fields (ticket_id, event, user) rather than using {k: v for k, v in data.items() if k != "sig"} — unknown extra fields must be rejected
- Add a
version field to the QR payload ("v": 2) so old hex-signed QR codes can be detected and rejected with a clear error message
- Write tests: extra unsigned field is rejected, old v1 hex signature is rejected with 200+
isValid: false
Suggested execution
git checkout -b security/qr-signature-hardening
- Update
src/utils.py compute_signature
- Update
src/main.py validate_qr handler
- Bump QR payload version
- Update all related tests
Guidelines
- This is a breaking change for existing QR codes — document the migration in the PR
- PR must include:
Closes #[issue_id]
- Timeframe: 72 hours
Description
compute_signatureinsrc/utils.pyreturnshmac.new(...).hexdigest()— a hex string. While HMAC-SHA256 is not directly vulnerable to length-extension attacks (unlike raw SHA256), using a hex digest doubles the output size unnecessarily and diverges from the JWT/PASETO convention of base64url encoding. More importantly, thevalidate-qrendpoint accepts the raw JSON-encoded QR string and extractssigby key name, making it possible to craft a valid-looking payload that passes JSON parsing but includes extra unsigned fields.Requirements & context
compute_signatureto returnbase64.urlsafe_b64encode(hmac_bytes).decode()instead ofhexdigest()validate_qrto rebuild the unsigned dict by explicitly whitelisting known fields (ticket_id,event,user) rather than using{k: v for k, v in data.items() if k != "sig"}— unknown extra fields must be rejectedversionfield to the QR payload ("v": 2) so old hex-signed QR codes can be detected and rejected with a clear error messageisValid: falseSuggested execution
src/utils.pycompute_signaturesrc/main.pyvalidate_qrhandlerGuidelines
Closes #[issue_id]