Skip to content

fix(authenticate-users): secure SIWE verification with domain validation#1503

Open
maho0638 wants to merge 1 commit into
base:masterfrom
maho0638:patch-4
Open

fix(authenticate-users): secure SIWE verification with domain validation#1503
maho0638 wants to merge 1 commit into
base:masterfrom
maho0638:patch-4

Conversation

@maho0638
Copy link
Copy Markdown

Security Fix

The authentication examples were using client.verifyMessage, which only verifies the signature cryptographically but does not check the SIWE message content (specifically the domain). This left applications following this guide vulnerable to cross-domain replay attacks (EIP-4361 violation).

Changes Made

  • Added import { verifySiweMessage } from 'viem/siwe'; to imports.
  • Replaced client.verifyMessage with verifySiweMessage in:
    1. Backend (Viem) example
    2. Example Express Server example
  • Now validates domain: req.headers.host to ensure the signature was intended for the correct application.

Fixes #1502

Replaced insecure client.verifyMessage with verifySiweMessage to prevent cross-domain replay attacks. Added domain validation in both Backend and Express Server examples. Fixes base#1502
@cb-heimdall
Copy link
Copy Markdown
Collaborator

🟡 Heimdall Review Status

Requirement Status More Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1

@Nexory
Copy link
Copy Markdown

Nexory commented May 28, 2026

The fix here is correct, but the same pattern exists in at least 5 other files in this repo. All call verifyMessage (or verifySiweMessage without domain/nonce params) on the backend without validating the SIWE domain field, leaving the same cross-domain replay exposure described in #1502.

Affected locations:

  1. docs/base-account/reference/core/capabilities/signInWithEthereum.mdx line 92 — client.verifyMessage({ address, message, signature })
  2. docs/base-account/reference/core/capabilities/signInWithEthereum.mdx line 175 — client.verifyMessage({ address, message, signature })
  3. docs/base-account/reference/core/capabilities/signInWithEthereum.mdx line 224 — client.verifyMessage({ address, message, signature })
  4. docs/base-account/framework-integrations/privy/authentication.mdx line 237 — client.verifyMessage({ address, message, signature })
  5. docs/base-account/framework-integrations/wagmi/sign-in-with-base.mdx line 134 — client.verifyMessage({ address, message, signature })
  6. docs/base-account/reference/ui-elements/sign-in-with-base-button.mdx line 461 — standalone verifyMessage({ address, message, signature }) (NextAuth example)

Suggested fix for all locations: replace with verifySiweMessage from viem/siwe, passing domain from the request Host header and the server-issued nonce:

import { verifySiweMessage } from 'viem/siwe';

const valid = await verifySiweMessage(client, {
  address,
  message,
  signature,
  domain: req.headers.host,   // validated against SIWE message domain
  nonce: serverIssuedNonce,   // validated against SIWE message nonce
});

This handles domain validation, nonce replay protection, and expiry in one call. Happy to extend the PR to cover these files if that would help.

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.

fix(docs): authenticate-users backend example missing domain validation — vulnerable to cross-domain replay attack

3 participants