Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions examples/with-next-siwe-iron-session/src/pages/api/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const { message, signature } = req.body;
const siweMessage = parseSiweMessage(message) as SiweMessage;

// Bind SIWE domain to this app host (parity with with-next-siwe-next-auth).
// Without this check, a signature minted for an attacker-controlled domain
// can be replayed against this verify endpoint under a stolen/own nonce.
const expectedHost = req.headers.host;
if (!expectedHost || siweMessage.domain !== expectedHost) {
Comment on lines +19 to +20

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bind SIWE messages to a trusted configured host

In self-hosted deployments where the proxy does not enforce a host allowlist, req.headers.host is supplied by the client and therefore cannot establish the application's identity. An attacker can request a nonce and retain the iron-session cookie while sending Host: attacker.example, have a victim sign that nonce for attacker.example, and submit the signature with the same spoofed header, so this check still accepts the cross-domain replay it intends to prevent. Compare against a configured public origin or an explicit trusted-host allowlist instead.

Useful? React with 👍 / 👎.

return res.status(422).json({ message: 'Invalid domain.' });
}

if (siweMessage.nonce !== req.session.nonce)
return res.status(422).json({ message: 'Invalid nonce.' });

const success = await publicClient.verifyMessage({
address: siweMessage.address,
message,
Expand All @@ -21,9 +32,6 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {

if (!success) throw new Error('Invalid signature.');

if (siweMessage.nonce !== req.session.nonce)
return res.status(422).json({ message: 'Invalid nonce.' });

req.session.siwe = siweMessage;
await req.session.save();
res.json({ ok: true });
Expand Down