v0.8.2: security hardening + Google prefill#14
Merged
Conversation
…ogle prefill
Response to a 0.8.x security audit plus a Google sign-in prefill fix.
Security:
- H1 NIP-98 replay protection: in-memory TTL set of consumed event ids in
nip98.verify_nip98; a login event can't mint a second JWT within its
freshness window (401 on reuse).
- H2 header-injection-resistant login URL: new VEZIR_PUBLIC_URL (config
public_url); _login_url pins to it instead of trusting X-Forwarded-Proto/
Host, so a caller reaching uvicorn directly can't spoof the NIP-98 u-tag
target. Falls back to header reconstruction when unset (local/dev).
- H3 hardening: Google Workspace-domain check is now exact (rsplit("@",1))
not a suffix endswith.
- M5: /health no longer returns data_dir (info disclosure).
- M4: loud startup warning when VEZIR_DISABLE_RATELIMIT is set.
- DST: _parse_iso (+ doctor/cli siblings) use calendar.timegm (true UTC)
instead of mktime(...) - time.timezone.
Fixed:
- Google prefill: Google's device endpoint returns only a bare
verification_url; device/start now synthesizes verification_url_complete
(.../device?user_code=<CODE>) so clients open a pre-filled page.
Deferred (audit, tracked in operator plan): JWT per-token revocation,
session-secret hot-rotation, NIP-46 "ack" tightening, infra LOWs.
Note: audit's H3 was essentially a false positive (endswith already anchors
on '@'); hardened anyway. Its "no google_auth tests" claim was stale —
test_google_auth.py has existed since PR #11.
11 new tests (replay reuse->401, VEZIR_PUBLIC_URL ignores spoofed headers,
exact-domain reject lookalike/subdomain, synthesized complete URL, /health
omits data_dir, _parse_iso UTC). Full suite 685 passed. ruff + mypy clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Response to a 0.8.x security audit, plus a Google sign-in prefill fix.
Security
nip98.verify_nip98now records consumed event ids (in-memory TTL set) and rejects reuse (401)._login_urlreconstructed the NIP-98u-tag target fromX-Forwarded-Proto/Host(spoofable if uvicorn is reached directly). NewVEZIR_PUBLIC_URL(configpublic_url) pins it; falls back to the old header behavior when unset. Set in prod.rsplit("@",1)) instead of a suffixendswith./healthno longer returnsdata_dir.VEZIR_DISABLE_RATELIMITis set._parse_iso(+ doctor/cli) usecalendar.timegm(true UTC) instead ofmktime(...) - time.timezone.Fixed
verification_url;device/startnow synthesizesverification_url_complete(…/device?user_code=<CODE>) so clients open a pre-filled page.Audit notes
endswith("@blinkbtc.com")already anchors on the@(verified:user@evilblinkbtc.com, subdomains, suffix tricks all fail); allowlist gates regardless. Hardened anyway.tests/test_google_auth.py(14 tests) has existed since PR Add Google sign-in for Blink members (OAuth device flow) #11.Tests
11 new (replay→401;
VEZIR_PUBLIC_URLignores spoofed headers; exact-domain rejects lookalike/subdomain; synthesized complete URL;/healthomitsdata_dir;_parse_isoUTC). Full suite: 685 passed. ruff + mypy clean.Version 0.8.1 → 0.8.2.