T09 ยท Insecure Skill Coding Practices
Error
- Location
- scripts/basemail-register.js:73
- Finding
- Unvalidated API-Controlled Message Signing<![CDATA[ ## Vulnerability Details **File Location**: `scripts/basemail-register.js:73-94` **Vulnerability Type**: Unrestricted signing of remote-controlled content **Risk Level**: High ### Vulnerable Code ```javascript const startRes = await fetch(`${API_BASE}/api/auth/start`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ address: wallet.address }) }); const startData = await startRes.json(); if (!startData.message) { throw new Error('Failed to start auth: ' + JSON.stringify(startData)); } // Sign message const signature = await wallet.signMessage(startData.message); // Verify const verifyRes = await fetch(`${API_BASE}/api/auth/verify`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: startData.message, signature, address: wallet.address }) }); return await verifyRes.json(); ``` ### Technical Analysis The script signs a message supplied entirely by `https://api.basemail.ai` without parsing or validating its contents. It does not verify that the message is a valid Sign-In with Ethereum statement containing the expected: - Domain: `basemail.ai` - URI: `https://basemail.ai` - Chain ID: `8453` - Wallet address - Nonce - Statement - Issuance and expiration times This differs from the protocol documented in `references/basemail-api.md`, which describes obtaining a nonce and constructing a fixed SIWE message. The implemented code instead treats any nonempty `message` value returned by `/api/auth/start` as safe to sign. Sending the public address, SIWE message, and resulting signature to BaseMail is necessary for the declared registration workflow. The private key and mnemonic are not transmitted. The vulnerability is that the remote service determines the unrestricted content to which the private key is applied, exceeding the minimum signing authority required for BaseMail authentication. ### Attack Path 1. A user invokes `basema ...[truncated 1349 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Retrieve only a nonce from the service and construct the complete SIWE message locally. 2. If the server must return the message, parse it with a maintained SIWE parser and reject it unless all security-sensitive fields match fixed expectations: - Domain exactly equals `basemail.ai`. - URI exactly equals `https://basemail.ai`. - Address exactly equals `wallet.address`, using canonical address comparison. - Chain ID exactly equals `8453`. - Nonce has the expected format and matches the nonce issued for the current flow. - Issuance time is recent. - Expiration time, if present, has not passed and is within a short allowed duration. - Statement and request ID are limited to explicitly approved values. 3. Reject malformed, duplicate, unexpected, or omitted SIWE fields. 4. Display the normalized message before signing. Require explicit confirmation if any nonstandard field is present. 5. Check `startRes.ok` and `verifyRes.ok` before processing response bodies. 6. Bind the nonce to one authentication attempt and prevent replay. 7. Keep the API origin fixed and do not permit environment variables or command-line arguments to override it without an explicit security warning. ]]>
