In service/src/api/register.ts, parseSiweMessage was called and chainId, nonce expiry, and cryptographic signature were all verified — but siweFields.domain was never asserted against the server's expected origin. A phishing site could fetch a fresh nonce from GET /v1/siwe/nonce (public), present a SIWE prompt for phish.example to the victim, then POST the resulting signature to the real API with attacker-controlled tradingWallet data. The server would accept it because ownership was proven on-chain. Fixed by extracting host[:port] from each CORS_ORIGINS entry into a siweAllowedDomains allowlist and asserting siweFields.domain is in that list before proceeding.
Applies to any backend that uses SIWE (EIP-4361) for authentication and then takes a side-effecting action (linking wallets, binding state) based on the proven address. The vulnerability exists even when nonce replay protection is correctly implemented — nonces are single-use but not origin-bound. The attack requires a 10-minute window (typical nonce TTL) and a realistic phishing scenario. Fix: after parseSiweMessage, assert siweFields.domain (and siweFields.uri host if present) against a server-side allowlist derived from the application's known frontend origins.