T09 · Insecure Skill Coding Practices
- Location
- index.js:42
- Finding
- Plaintext Storage of Nostr Private Keys with Unrestricted Default Permissions<![CDATA[ ## Vulnerability Details **File Location**: `index.js:42-47` and `index.js:67-76` **Vulnerability Type**: Plaintext sensitive-data storage **Risk Level**: High ### Vulnerable Code ```js function saveIdentities(identities) { ensureDataDir(); fs.writeFileSync(IDENTITIES_FILE, JSON.stringify(identities, null, 2)); } ``` ```js // 存储私钥(hex格式) const skHex = Buffer.from(sk).toString('hex'); identities[userId] = { privateKey: skHex, publicKey: pk, npub: npub, name: name || `user_${userId.slice(0, 8)}`, createdAt: new Date().toISOString() }; saveIdentities(identities); ``` ### Technical Analysis The Skill stores every user's Nostr private key as an unencrypted hexadecimal string in `data/identities.json`. The same file also contains identity metadata, interests, and nickname mappings. `fs.writeFileSync` is invoked without an explicit restrictive file mode. The data directory is likewise created without an explicit mode. Consequently, access is governed by the process umask and existing filesystem permissions, which may allow other local users or processes to read the file. The implementation does not validate file ownership or permissions before loading existing data. Because a Nostr private key directly controls an identity, this is equivalent to storing an authentication credential in plaintext. ### Attack Path 1. A local attacker, compromised dependency, co-hosted service, backup process, or other process gains read access to the Skill directory. 2. The attacker reads `data/identities.json`. 3. The attacker extracts the hexadecimal `privateKey` value for one or more users. 4. The attacker imports the key into another Nostr client or uses `nostr-tools` to sign arbitrary events. 5. The attacker impersonates the victim and may decrypt applicable NIP-04 direct messages. ### Impact Assessment Disclosure of a private key provides permanent control over the associated Nostr identity. The attacker can: - Publish signed posts ...[truncated 442 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Store private keys in an operating-system keychain, hardware-backed keystore, or dedicated secrets manager. 2. If file-based storage is unavoidable, encrypt each private key using an authenticated encryption scheme and keep the encryption key outside the project directory. 3. Create the data directory with mode `0700` and identity files with mode `0600`. 4. Verify file ownership and permissions before reading an existing identity file; refuse operation if they are unsafe. 5. Use atomic writes through a securely created temporary file followed by a rename. 6. Separate each user's secrets rather than placing all users' private keys in one JSON document. 7. Avoid unnecessary serialization or logging of private-key material. 8. Document key rotation and incident-response procedures for users whose local identity file may have been exposed. ]]>
