Skip to content

fix: reject zero sessionId in verifySession - #882

Merged
paolodamico merged 5 commits into
mainfrom
kilianglas/verifier-v3-reject-zero-session
Aug 5, 2026
Merged

fix: reject zero sessionId in verifySession#882
paolodamico merged 5 commits into
mainfrom
kilianglas/verifier-v3-reject-zero-session

Conversation

@kilianglas

@kilianglas kilianglas commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

verifySession accepts sessionId == 0, which is the circuit's "no session" sentinel and is satisfiable by any World ID. A session record left at zero can therefore be satisfied by an arbitrary World ID, voiding the "same user + RP pair across requests" continuity guarantee that verifySession is meant to provide.

Adds WorldIDVerifierV3, which overrides verifySession to reject a zero sessionId. Purely additive — three new files, no existing file is modified.

Reported via HackerOne #3912490.

Why the sentinel is universally satisfiable

circom/client_side_proofs/oprf_nullifier.circom:183 constrains the commitment as a disjunction:

id_commitment * (id_commitment - computed_id_commitment) === 0;

It is satisfied either when id_commitment == H(DS_C, mt_index, id_commitment_r) or when id_commitment == 0. The zero branch places no constraint on mt_index, so it is not merely "unbound" — it is provable by any identity in the tree. Every Uniqueness Proof uses that branch legitimately, so the sentinel cannot be removed from the circuit; the verifier has to reject it at the boundary instead.

The id_commitment circuit constraint is a disjunction: it holds when the
commitment equals H(DS_C, mt_index, r) or when it is zero. Zero is the
"no session" sentinel every Uniqueness Proof uses, and it is satisfiable by
any World ID regardless of mt_index. A session record left at zero can
therefore be satisfied by an arbitrary World ID with a patched
authenticator, voiding the same user+RP continuity guarantee.

V2 is released and immutable, so the check lands in a new WorldIDVerifierV3
that overrides verifySession. Existing deployments are remediated by
upgrading the proxy to V3.

Reported via HackerOne #3912490.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@kilianglas
kilianglas requested a review from a team as a code owner August 4, 2026 11:49
kilianglas and others added 2 commits August 4, 2026 15:10
Follows the error moving onto the V2 interface.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread contracts/test/core/WorldIDVerifierV3Test.t.sol Outdated
Comment thread contracts/src/core/UnreleasedWorldIDVerifierV3.sol
The fuzz test forces the 0x02 prefix and covers the same assertion, so the
fixed-action case adds no coverage. Its rationale comment moves to the fuzz
test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
paolodamico
paolodamico previously approved these changes Aug 5, 2026
Comment thread contracts/src/core/UnreleasedWorldIDVerifierV3.sol
Prefixes the file names so V3 is not mistaken for a shipped version. Drop the
prefix once it ships. Contract and interface names are unchanged, so the
deployed identity and ABI are unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@paolodamico
paolodamico merged commit 390976f into main Aug 5, 2026
24 checks passed
@paolodamico
paolodamico deleted the kilianglas/verifier-v3-reject-zero-session branch August 5, 2026 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants