T09 · Insecure Skill Coding Practices
Error
- Location
- assets/template/src/server.cjs:42
- Finding
- Customer Wallet Private Keys Stored in Plaintext<![CDATA[ ## Vulnerability Details **File Location**: `assets/template/src/server.cjs:42-48, 91-101, 215`; `assets/template/src/server.js:42-48, 91-101, 189` **Vulnerability Type**: Plaintext storage of cryptographic private keys **Risk Level**: High ### Vulnerable Code ```js CREATE TABLE IF NOT EXISTS deposit_wallets ( telegram_user_id TEXT PRIMARY KEY, deposit_pubkey TEXT NOT NULL, deposit_secret_b58 TEXT NOT NULL, created_at INTEGER NOT NULL ); ``` ```js function getOrCreateDepositWallet(telegramUserId) { ensureUser(telegramUserId); const row = db.prepare('SELECT deposit_pubkey, deposit_secret_b58 FROM deposit_wallets WHERE telegram_user_id=?') .get(telegramUserId); if (row) return row; // DEMO: generate a new keypair per user and store secret in sqlite const kp = Keypair.generate(); const secretB58 = bs58.encode(kp.secretKey); db.prepare('INSERT INTO deposit_wallets (telegram_user_id, deposit_pubkey, deposit_secret_b58, created_at) VALUES (?,?,?,?)') .run(telegramUserId, kp.publicKey.toBase58(), secretB58, Date.now()); return { deposit_pubkey: kp.publicKey.toBase58(), deposit_secret_b58: secretB58 }; } ``` ```js const payerKeypair = Keypair.fromSecretKey( bs58.decode(w.deposit_secret_b58) ); ``` ### Technical Analysis The application generates a Solana keypair for each Telegram user and stores the complete secret key in the SQLite database as a Base58 string. Base58 is a reversible representation and provides no confidentiality or integrity protection. The implementation does not establish restrictive database file permissions, encrypt individual private keys, use an operating-system key store, or delegate signing to a hardware-backed key management service. Consequently, access to `demo.db`, a database backup, or a filesystem snapshot is sufficient to recover every stored deposit-wallet private key. This custodial key storage is also inconsistent with the Skill's declared rule that private keys must never be handle ...[truncated 885 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove server-generated custodial deposit keypairs and require users to sign transactions through their own wallets. 2. Return unsigned payment instructions or transactions to the client, then verify the submitted transaction server-side. 3. If custodial signing is an unavoidable business requirement: - Store keys in a dedicated KMS, HSM, or encrypted keystore. - Use envelope encryption with independently managed encryption keys. - Restrict signing policies by destination, amount, and transaction type. - Apply restrictive filesystem and database permissions. - Prevent private keys from entering logs, backups, crash dumps, or monitoring data. - Implement key rotation and a documented incident-response process. 4. Explicitly document the custodial security model and obtain informed user consent. ]]>
